Data collected
The complete list of the data Korvus collects, before and after cookie consent, with the CNIL classification of each field.
Philosophy
Korvus collects the bare minimum needed to detect your site's revenue leaks. Every piece of data collected serves directly to identify a problem and to calculate its impact in euros.
Korvus is not a behavioral analytics tool: it builds no individual heatmaps or user profiles, and does no fingerprinting or cross-domain tracking. It does not record browsing continuously. On a detected incident, and only if you have turned on the video proof module, it can produce a video proof whose text is masked before it leaves the browser, and which can only be viewed from that incident's case file. The video produced lasts at most 94 seconds. The code enforces this upper bound; a typical proof is much shorter, and there is no minimum length.
This module is optional and turned on site by site. It changes what is collected and what the snippet does on the page: the "Video proof module" section below covers it in full.
The snippet reads structured signals in your site's DOM: action button text, product names and prices, visible error messages, promo codes, payment method labels, out-of-stock statuses. It never reads free page content, data your visitors enter, or personal information displayed in customer accounts.
The snippet observes, the server calculates
Our architectural principle is non-negotiable: no business calculation, no classification and no detection logic runs in your visitor's browser. The Korvus snippet is limited to passive observers (standard browser APIs, read-only DOM access). It sends raw signals to the Korvus server, which makes the decisions. As a result, the impact on your site's speed and responsiveness (TBT, INP) stays undetectable, including in real-world conditions.
In practice, the snippet:
- Only runs after the page has fully loaded (
window load+setTimeout(0)) - Never writes to the DOM and never forces a reflow (one exception if you have turned on the video proof module: see "Video proof module" below)
- Switches itself off after 30 minutes of inactivity and resumes on the first interaction
What Korvus does not collect
- Form content: no reading of emails, passwords, postal addresses, bank card numbers, order numbers or any personal information entered by the user
- Payment fields: no access to payment iframes (Stripe Elements, Adyen, PayPlug, PayPal), no reading of CVV, card number or 3D Secure code
- Content of
login,accountand pro/B2B pages: on these identifying pages, the snippet collects the same anonymized technical signals as everywhere else (hashed selectors, errors, friction points), but never reads their content: no name, no email, no password, no order history, no saved payment methods, no entered data or displayed personal information - HTTP request and response bodies: Korvus observes failing network requests to extract technical metadata (URL without parameters, method, status code, duration, response content type), but never reads a request body, a response body or authentication headers
- Cookies: Korvus reads none of your site's cookies (except
korvus_optout, which it sets itself when a visitor objects) - localStorage / sessionStorage: no access to your site's local storage, except a technical read of structural cart keys (
cart,shopify_cart,woocommerce_cart_hash) to confirm an add-to-cart - Continuous browsing recording, individual heatmaps, keystrokes, cross-domain tracking: never collected, under any configuration. An incident video proof is none of these: it is only kept if an incident occurred, it covers the pageview where it happened (and the previous page in the case of an error), the video produced lasts at most 94 seconds, and its text is masked before it leaves the browser.
- Mouse movements, click coordinates, scrolling: never collected by the base snippet, under any configuration. Friction (clicks with no effect, repeated clicks) is detected locally and sent in aggregated form, with no coordinates or content. The video proof module does collect them when you turn it on: see "Video proof module" below.
- Browser fingerprint (fingerprinting): no canvas, no audio fingerprinting, no collection of installed fonts, no
navigator.hardwareConcurrency, nomemory - Raw screen resolution: only the bucketed viewport width is collected (see below)
- IP address: never stored (see the dedicated section)
- Cross-session, cross-tab, cross-domain tracking: each tab is independent, with no correlation between visits
- New vs. returning visitor: incompatible with our sessionStorage model, not collected
Video proof module
This module is off by default. It is only switched on if you ask for it, site by site. As long as it is not turned on, none of the following applies to your site, and its code is not even downloaded by your visitors' browsers.
Once turned on, it lets Korvus attach to a detected incident a short video of what happened on screen, so your developer sees the defect instead of having to reproduce it. This capability means collecting more, and the lists below spell out exactly what.
What this module adds to collection:
- Pointer positions and the coordinates of every click. The base snippet never collects these. Without them, the video would show neither the visitor's gesture nor the element they aimed at, and would no longer prove anything.
- The page structure and its changes during capture, in order to replay the screen. Text is masked at recording time, before it leaves the browser. Input fields are always masked, and no setting can reveal a value entered by a visitor.
What limits this module:
- Consent required. No capture is recorded if the visitor has not consented, including when the triggering incident is itself exempt from consent. An exempt signal does not make the video it triggers exempt.
- No continuous recording. A capture is only kept if an incident occurred; it covers the pageview concerned (and the previous one in the case of an error), not the entire journey.
- A length capped by the code. The delivered video lasts at most 94 seconds, a ceiling applied when the file is produced. There is no minimum length: a proof can be much shorter.
- Viewable only from the incident's case file, never as a library of sessions to browse.
What this module does on the page, departing from our "zero DOM changes" principle:
- It adds a
<script>tag to the<head>to load its own code from our CDN. It is the only way to do this without requiringunsafe-evalin your Content Security Policy. - The recording library creates a hidden
iframeto read browser APIs that other scripts on the page have not altered. Thisiframeis invisible, loads no page, and is removed when recording stops (on WebKit browsers, it stays in place for the whole capture).
These two elements have no visual effect and do not change how your page renders, but they do write to the DOM: the statement "Korvus never changes the DOM" holds for the base snippet, not for a site where video proof is turned on.
CNIL transparency: two classes of data
Korvus is an audience measurement and technical issue detection solution. As such, part of our data is exempt from consent under Article 82 of the French Data Protection Act, according to the criteria set by the CNIL's July 2025 self-assessment tool and the EDPB Guidelines 2/2023 on Article 5(3) of the ePrivacy Directive.
The exempt purposes cover:
- Measuring technical performance (Core Web Vitals, load times)
- Detecting navigation problems (JavaScript errors, failed HTTP requests)
- Optimizing the site's UX
Any data with a marketing purpose (conversion measurement, ad campaign performance, acquisition channels, individual monetary values) requires the visitor's consent.
What still works when visitors refuse cookies
In the "site reactions" category (JS errors, failed HTTP requests, broken resources, error messages, CSP violations, invalid structured data), 6 out of 9 signals are captured with no cookie and no consent. In the "user actions" category, half of the signals are also captured in exempt mode (add-to-cart attempts, add-to-cart confirmations, search structure, visible promo codes).
In practice, for visitors who refuse cookies, Korvus keeps detecting the most critical leaks (checkout errors, broken buttons, out-of-stock products, 404 pages in the funnel), while most tools lose 30 to 40% of their signals.
Visitor identification
Korvus sets no cookie in normal operation.
The session identifier is an anonymous identifier generated locally by the visitor's browser and stored in sessionStorage. In practice:
- The identifier is created when the tab opens
- It is destroyed when the tab closes
- It is not shared between tabs
- It is not persisted between visits
- Two sessions from the same visitor cannot be linked
Korvus does no fingerprinting and never cross-references data between sessions.
IP address
The visitor's IP address is used only once, server-side, to determine the country code (e.g. FR, BE, CH) and the preferred language (through the Accept-Language header). It is deleted immediately after this operation:
- The IP is never stored in a database
- The IP is never truncated (a truncated IP would still be personal data)
- The IP is never passed to a third-party service
Data collected without consent
This data is exempt from consent as audience measurement and technical issue detection. It is collected on 100% of your traffic.
Session
Data collected once per visit (one tab = one session):
| Data | Example | Purpose |
|---|---|---|
| Landing page path | /produit/air-max-90 | Identify the entry page, never the full URL, never the URL parameters |
| Referrer domain | google.com | Identify the traffic source (host only, never the full URL) |
| Entry channel | organic_search / social / email / referral / direct | Classification derived server-side from the referrer (no additional data captured) |
| Country | FR | Segment problems by geographic area |
| Preferred language | fr-FR | Detect locale-specific bugs (e.g. a checkout broken for fr-BE). Read from the Accept-Language header server-side |
| Device type | mobile | Detect problems specific to a device type |
| Viewport width (bucketed) | 769 (for 769-1024 px) | Identify bugs tied to a responsive breakpoint. 6 buckets only, no exact value |
| Dark mode | true / false | Detect conversion drops tied to dark mode display (invisible CTAs, unreadable prices) |
| Operating system | iOS 17 | Identify incompatibilities (major version only) |
| Browser | Chrome 124 | Identify incompatibilities (major version only) |
| Connection type | 4g | Put performance problems in context |
| Ad blocker detected | true / false | Tell a truly broken marketing pixel from one blocked by the user. Without this field, we would generate ~30% false positives on desktop |
| Pixels loaded (list) | ["meta", "ga4"] | List of known pixels that loaded successfully on the page |
| Pixels blocked (list) | ["tiktok"] | List of known pixels whose resource failed at network level (ad blocker, CSP, CDN unavailable). A technical load measurement, no user tracking |
| Bot detected | true / false | Filter bots out of detections |
Pageviews
Data collected on every page change:
| Data | Example | Purpose |
|---|---|---|
| Page path | /produit/air-max-90 | Locate the problem. URL parameters are kept separate (see consent section) |
| Page type | pdp (product page) | Segment by journey step |
| HTTP status | 200, 404, 500 | Detect error pages receiving traffic (a massive leak on a paid campaign pointing to a deleted PDP) |
| Error page | true / false | Flag derived from the HTTP status and DOM signatures (title "404", "erreur", "not found") |
| Navigation type | navigate / reload / back_forward / prerender | Detect users reloading a broken payment page (a very strong bug signal) |
| Actual visible time | 12400 ms | Time the page was actually in the foreground (active tab), not the time it sat open in the background |
| Maximum scroll depth | 85 % | Detect pages whose main content (buy button, form) can no longer be reached after a deployment |
| User interaction | true / false + time_to_first_interaction_ms | Tell a page that was "seen but dead" (broken JS, no click possible) from a page used normally |
| Product identifier type (PDP) | dataLayer / jsonld / dom / slug | Source of the detected product identifier (catalog SKU) |
| Product identifier (PDP) | REF-12345 | SKU or external identifier of the displayed product. Catalog data, zero personal data |
| Product name (PDP) | Air Max 90 — Taille 42 | Human-readable name shown in alerts and the dashboard. Used for messages such as "Out of stock on Air Max 90 with 42 sessions/h" |
| Product availability (PDP) | true / false / null | Stock status observed through JSON-LD, the add-to-cart button or visible text |
| Time to First Byte | 320 ms | Detect server slowdowns |
| First Contentful Paint | 1200 ms | Measure initial display speed |
| Largest Contentful Paint | 2500 ms | Measure perceived load speed |
| LCP element | { tag: "img", selector: "section.hero > img.banner", url, area_px: 870400 } | Pinpoint the element slowing down the load and keep its visual area in px² (a single element per page, the one that caused the LCP measurement) |
| Interaction to Next Paint | 180 ms | Measure responsiveness to interactions |
| INP element | { selector: "button.add-to-cart", event_type: "click", processing_ms, delay_ms } | Identify the slow interaction (a single element per page, the one that caused the INP measurement) |
| Cumulative Layout Shift | 0.12 | Detect visual shifts |
| Largest CLS shift | { selector: "div.ad-banner", shift_value: 0.18, had_recent_input: false } | Identify the element responsible for the largest visual shift (a single element per page) |
| Long tasks | Count + total_blocking_time_ms | Measure the time a script blocks the browser (complements network time) |
| Bfcache restored | true / false | Detect when a third-party script breaks the browser cache (a massive degradation of back navigations) |
| Slow resources | Top 10 resources taking more than 100 ms to load, as an array { url, type, duration_ms, transfer_kb, blocking } | Identify the scripts, images, stylesheets or fonts slowing down the page |
| Loaded scripts fingerprint | SHA hash of the URLs | Detect front-end deployments and correlate them with new errors |
| Error counters on the page | js_error_count: 2, request_error_count: 1, resource_error_count: 0, ux_error_count: 0 | Aggregated summary calculated server-side from the detailed error events |
The page types detected are: home, plp (product listing), pdp (product page), cart, checkout (generic), checkout_info, checkout_shipping, checkout_payment, order_confirmation, login, account, search, other.
login, account and pro/B2B pages: field-level masking, not page-level blocking
On these identifying pages, Korvus collects the same anonymized technical signals as on the rest of the site (hashed selectors, errors, friction points). This is the market standard (Contentsquare, Datadog RUM, Sentry): the right safeguard is masking at field level, not blocking at page level. The CNIL itself explicitly names technical error detection and the identification of friction points as legitimate purposes.
The safeguards applied on these pages are structural:
- No content reading (
no-body-read): never the page body text, never an entered value (.value), never the body of an HTTP request or response, never an authentication header. - Hashed, hardened selectors: a clicked element is identified by a technical hash;
ididentifiers carrying a first or last name (bonjour-jeandupont,jean.dupont) are rejected before anything is sent. - Field-level anonymization: the rare text strings that are sent are stripped of any personal data (email, phone number, IBAN, card number).
Korvus therefore never reads the content, entered values or displayed personal information of these pages: it only captures anonymized structural technical signals from them.
Events: site reactions
Korvus captures 6 types of technical signals emitted by your site, with no consent required:
| Event | What is captured | Purpose |
|---|---|---|
js_error | Exception name (TypeError, ReferenceError...), message, source file, line and column numbers, stack trace truncated to 500 characters, first-party vs. third-party script flag, time since the last interaction | Detect bugs that block the journey. Stack traces never contain user data |
request_error | URL host and path (never the URL parameters, stripped in the browser), HTTP method, status code, duration, response content type, third-party origin flag, type (fetch / xhr) | Detect broken APIs. Query params are always stripped before sending to prevent any token or email leak |
resource_error | Tag type (img, script, iframe...), host and path, third-party flag, iframe flag, time before failure | Detect missing resources, broken payment iframes (Stripe, Adyen, PayPal), unavailable critical widgets |
ux_error | Text of the visible error message (truncated to 200 characters), CSS selector of the element, inside-a-form flag, actual visibility flag | Detect error messages shown to the visitor ("Une erreur est survenue", "Paiement refusé", "Stock indisponible") |
csp_violation | Violated directive, host and path of the blocked resource, effective directive, mode (enforce / report) | Tell a resource blocked by your Content Security Policy from a broken one. Enables precise diagnostics such as "your CSP is blocking the Meta pixel, add connect.facebook.net to script-src" |
structured_data_check | Presence of Schema.org Product, presence of Schema.org Offer, missing fields, errors, product identifier and name (if present in the JSON-LD), availability | Check product page SEO compliance and detect inconsistencies between the schema sent to Google Merchant and the price actually displayed |
All of this data is pure technical measurement. None of it concerns the individual visitor's behavior.
Events: user actions (exempt mode)
Half of the monetizable user actions are captured without consent, as raw technical facts, with no monetary value. Values (amounts, discounts) are captured separately, with consent.
| Event | What is captured | Purpose |
|---|---|---|
add_to_cart_attempt | Click detected on an add-to-cart button, product identifier, success flag | Detect broken buttons that no longer respond |
add_to_cart_succeeded | Add-to-cart confirmation, detected by passive DOM observation (cart counter incremented, success notification, cart drawer opening, URL change, mutation of a structural cart key in localStorage), latency between the click and the confirmation | Measure the real add-to-cart success rate on 100% of traffic. This is the signal that reveals silent cart API bugs |
promo_applied (DOM source) | Promo code visible on the page | Detect promo code overuse (shared on a forum or a cashback extension). The discount amount is captured separately, with consent |
payment_method_selected | Raw signals describing the chosen payment method: displayed label, technical input value, icon path if present, detection cascade type | Measure the real payment mix and detect a payment method that disappears from the DOM after a PSP integration bug. Normalization into a category (card, paypal, apple_pay, alma...) is done server-side. No reading of bank card fields or opaque PSP iframes |
payment_attempted | Click detected on a "Pay" / "Place order" button or equivalents in 15 European languages, text of the clicked button (truncated to 100 characters), detection cascade type | A pure technical fact, "payment button clicked", with no monetary value. Needed to detect clicks that fail silently (the most expensive case at checkout) |
promo_code_rejected | Code entered, rejection signal (error message, CSS class, code not applied) | Detect a promo code broken in the back office, or a brute-force attempt on codes |
shipping_method_selected | Raw signals describing the chosen shipping method (label, input value, cascade type) | Detect a broken carrier or a missing option that forces users to abandon |
3ds_initiated / 3ds_completed | 3D Secure context: a challenge iframe appearing, disappearing, navigation to an order confirmation. No reading of the iframe content, no reading of OTP codes, no reading of cards | Measure 3D Secure challenge success rates by issuer and detect a bank whose flow is broken |
variant_selected | Dimension chosen on a product page (size, color, format), selected value | Detect a variant that is systematically unavailable after selection (stock / UI sync bug) |
A deliberate limit
Some modern payment systems render their fields in a fully opaque cross-origin iframe (Stripe Payment Element, Adyen Drop-in, Mollie Components). In these cases, Korvus only captures the signal "the user interacted with the payment block", with no detail on the exact method. This is a deliberate choice: no technique for inspecting these iframes would be compatible with the GDPR or with our pure-observation principle.
Data collected with consent
This data requires the visitor's consent (cookies accepted). If consent is not granted, it is simply not collected, and Korvus neither estimates it nor falls back on another source.
Session (additional)
| Data | Example | Purpose |
|---|---|---|
| UTM source | facebook | Identify the ad channel |
| UTM medium | cpc | Identify the campaign type |
| UTM campaign | soldes-ete-2026 | Identify the specific campaign |
| Paid traffic detected | true / false | Segment problems by traffic type |
Meta click identifier (fbclid) | Present: yes / no | Detect Meta Ads traffic (the value is not stored) |
Google click identifier (gclid) | Present: yes / no | Detect Google Ads traffic (the value is not stored) |
TikTok click identifier (ttclid) | Present: yes / no | Detect TikTok Ads traffic (the value is not stored) |
| Landing URL parameters | { "utm_source": "google", "color": "red" } | All entry URL parameters. Not collected without consent |
| Full OS version | iOS 17.4.1 | Fine-grained bug analysis by version |
| Full browser version | Chrome 124.0.6367.91 | Fine-grained bug analysis by version |
Pageviews (additional)
| Data | Example | Purpose |
|---|---|---|
| Page URL parameters | { "variant": "42", "utm_campaign": "soldes" } | Fine-grained analysis of pages with parameters. Not collected without consent |
| Observed product price (PDP) | 49.90 EUR | Detect price inconsistencies between pages and catastrophic errors (a product displayed at €0.99 instead of €99). Financial data, consent required |
Marketing and dataLayer events
| Event | What is captured | Purpose |
|---|---|---|
tag_fired | Normalized tag (meta_pixel, google_ads, google_analytics, tiktok_pixel, pinterest_tag, linkedin_insight, bing_ads, criteo, other), event type (PageView, Purchase, AddToCart...), status (fired / failed / blocked), sanitized pixel identifier (never a session token) | Check that each marketing pixel fires correctly and at the right granularity |
datalayer_validation | Event name, validity, missing fields, null fields, item count, presence of a value | Check the integrity of e-commerce dataLayer pushes before they feed your analytics tools |
datalayer_unknown | Name of an unknown dataLayer event with an e-commerce structure (5 per session at most) | Onboarding tool to automatically detect custom dataLayers |
purchase | Transaction amount, currency. Source: a dedicated GTM tag you install yourself on the confirmation page (see HTML installation). The snippet never reads request bodies or page content. If you also have a GA4 enhanced ecommerce dataLayer push (event: 'purchase'), Korvus observes it too, to cross-check your marketing tracking, but euro figures rely exclusively on the dedicated tag, which is what lets us alert you if your GA4 breaks | Calculate the real financial impact of the leaks detected |
add_to_cart | Product identifier, name, amount, currency | Measure the real value of the conversion rate |
promo_applied (dataLayer source) | Promo code, discount amount, discount type | Measure the financial impact of promotions |
Why the exempt / consent split?
Every monetizable user action is captured in two layers:
- The raw fact, in exempt mode: on 100% of traffic, it feeds the structural detection of leaks (failure rate, abandonment, outages)
- The monetary value, with consent: on around 70% of consented traffic, it feeds the euro figures
As a result, the most critical leaks are detected across your entire audience, and the financial impact is honestly extrapolated from a statistically representative sample. You never miss a leak signal because a visitor refused cookies.
Your site's reference data
On top of traffic data, Korvus maintains a technical reference of your site server-side, populated automatically:
- List of expected pixels (
expected_pixels): initially built by an automated onboarding crawl (a technical analysis of your home page and a few product pages), then updated continuously. It is used to tell a marketing pixel that was never installed on your site from one that is genuinely broken. This list is inferred from traffic data already collected and triggers no additional collection. - Passive product catalog: built from visited PDPs (identifier, name, availability, history of price and stock changes). No sync with your backend is needed, and there is no access to your database.
Retention period
All data is kept for 13 months at most, in line with CNIL recommendations. Deletion is automatic (monthly partitioning, with partitions older than 13 months dropped).
Right to object
Even for consent-exempt data, your visitors can refuse Korvus collection. As the site publisher, you must add a link to your privacy policy that sets the korvus_optout=1 cookie.
When this cookie is present, the Korvus snippet does not initialize at all: no data is collected, no network request is sent, no observer is attached.
Data hosting
All data is hosted in France (Scaleway, Paris region) and is never transferred outside the European Union. Korvus acts as a processor within the meaning of Article 28 of the GDPR. Each customer's data is strictly isolated and is never pooled or reused for our own purposes.
Last revised: August 9, 2026 (description of the video proof module: pointer and click coordinates, capped length, DOM writes). This page reflects version V2 of the Korvus snippet. Transaction amounts are now captured through a dedicated GTM tag you install yourself on the order confirmation page (installation guide). CNIL classification based on the CNIL self-assessment tool (July 2025) and the EDPB Guidelines 2/2023 on the application of Article 5(3) of the ePrivacy Directive.