Skip to main content
8 min read

Privacy-first

Zenovay is built to collect as little as possible about the people who visit your site. This page sets out what the tracker stores, what it refuses to store, and which signals it obeys, so you can check that against what your own site needs.

This page describes what the product does. What that means for your obligations depends on your jurisdiction, your audience and your legal basis.


Device storage

Whether the tracker puts anything on a visitor's device is a per-site setting called cookieless mode. It is the pre-selected option when you add a website, so a site created today is cookieless unless you chose the first-party cookie mode at that moment. Websites created before cookieless became the default keep whichever mode they were on; changing the setting later is a one-click switch in your website settings.

Cookieless modeWhat is written to the device
OnNo cookies and no localStorage. The visitor ID lives in memory, scoped to the window, and is gone when the tab closes. On sites running an exit-intent widget, one sessionStorage marker (__zv_widget_shown_session) records that the widget already fired, so the visitor is not shown it twice.
OffOne first-party visitor cookie plus a small number of localStorage keys.

Neither mode sets a third-party cookie, and there is no cross-site advertising network behind the tracker.

Cookieless mode governs device storage and nothing else. Session replay, heatmaps, screenshots, error tracking, frustration detection, live cursor, B2B company identification and visitor value scoring all keep running on a cookieless site, because none of them depend on writing to the device. Whether you want those running is a separate decision, and the section on behavioural capture below covers the gate for the two heaviest of them.

Global Privacy Control

Zenovay honours Global Privacy Control. You do not have to call anything to switch it on.

When a browser reports GPC through navigator.globalPrivacyControl === true, the tracking script stops before any capture begins. The initialisation routine returns at that check, so nothing after it ever runs: no page views, no events, no session replay, no heatmap capture, no error tracking, no Core Web Vitals, no diagnostics. Nothing is sent from that browser at all.

Requests that reach the API another way, by direct HTTP call or through the server-side events API, are handled by a second check on the Sec-GPC: 1 request header. On those requests Zenovay suppresses the behavioural enrichment layer: demographic and language inference, B2B company enrichment, milestone behavioural events, live-cursor ingest and popup-widget impressions. A server-side event batch carrying the header is rejected outright with the reason gpc_opted_out.

Requests that arrive over plain HTTP with Sec-GPC: 1 are still recorded as a visitor row, flagged gpc_opted_out: true, carrying the visitor and session identifiers, the IP hash, and the geolocation, device and campaign fields. The signal changes what is derived about the visitor, not whether the request is logged. For visitors coming through the tracking script this never arises, because the script sent nothing in the first place.

ignoreDNT does not apply to GPC. There is no setting, on any plan, that makes the tracker capture from a browser reporting Global Privacy Control.

Do Not Track

The legacy navigator.doNotTrack signal is honoured by default: the tracker aborts when the browser reports 1. Unlike GPC, it is overridable through the ignoreDNT option or the data-ignore-dnt attribute.

The first-party proxy setup sets ignoreDNT automatically. Every bundle served through the proxy ships with it enabled, so a first-party-proxied site does not honour Do Not Track, whether or not you asked for that. If Do Not Track matters to you, serve the standard script from api.zenovay.com instead. Global Privacy Control is unaffected either way and is still honoured on proxied sites.

Do Not Track is purely a client-side signal. Nothing on the server reads a DNT request header, so a request that reaches the API is processed regardless of what the header said.

IP addresses

The analytics tables store a hash, never the address. visitors.ip_address is written null, the plaintext column was dropped from the audit log, and the team audit log hashes on insert. Nothing in your dashboard, your exports or the API returns a raw visitor IP.

The address does exist in plaintext before that point, and briefly afterwards, in places worth knowing about:

  • Geolocation lookup. The raw address is sent to the geolocation providers ipinfo.io and ipwho.is to resolve country, region and city, and the result is cached under a key containing the address for 24 hours.
  • Abuse and rate limiting. Rate-limit counters, IP blocklist entries, ban records and security event records are keyed on the address, with lifetimes from 24 hours to 30 days. A small number of security tables store it directly.
  • Edge logs. Rate limiting and security events log the address to Cloudflare Workers logs unmasked.

The visitor identifier is a SHA-256 hash over the address, the website ID and the current UTC date. Because the date is part of the input, the identifier changes at midnight UTC, and the analytics tables therefore hold no field that links a visitor's activity across two days. That is a property of what is stored, not a guarantee that the value could not be recomputed: the hash input is currently the address, the site and the date, with no server-side secret mixed in, so anyone already holding a raw address could reproduce the corresponding identifier. Treat the identifier as pseudonymous rather than anonymous.

Two identifiers behave differently and are worth calling out. The B2B company hash is deliberately not rotated, because correlating company visits over time is the entire point of the feature. And in first-party cookie mode the cookie, not the hash, is what carries identity across days.

Behavioural capture is gated separately

Cookieless mode answers the device-storage question, and only that one. Recording a visitor's screen is individual-level processing whether or not a cookie was involved.

Session replay and heatmaps therefore have their own gate. On a site with the requirement switched on, neither records until your consent mechanism passes a granted decision through zenovay('consent', ...). See Consent for Behavioural Capture.

One detail matters when you reason about what a recording contains: input values are masked by default and that cannot be turned off, and password and payment-card fields are blocked from capture entirely, but text the page renders is not masked by default. A confirmation page that prints a name, an address or an order number puts that text in the recording unless you switch text masking on. See Session Replay.

Data retention

Analytics data is kept for a fixed window per plan, then deleted:

PlanAnalytics retentionSession replay and heatmaps
Free1 yearNot available
Pro2 years60 days
Scale4 years120 days
EnterpriseConfigurable180 days

Deletion runs in three stages, not one. Expired data is soft-hidden by a daily job at 02:00 UTC, which removes it from every dashboard, export and API response immediately. It then sits in a 30-day grace period, during which a second daily job at 04:00 UTC hard-deletes anything whose grace period has run out. Notification emails go out around the transition, at day 0, day 23 and day 27, so an expiry is never silent.

Other data has its own clocks: audit logs are purged after 24 months, and support tickets, ticket messages and AI conversations after 2 years. Deleting your account is an immediate hard delete with no grace period.

Deleting a visitor's data

All data for an individual visitor can be deleted from a website on request. The action is irreversible, and it is available through the request_data_deletion MCP tool in read-write mode.

To pull your own data out rather than delete it, see Data Export.


Questions about privacy? Contact our team at support@zenovay.com.

Was this page helpful?