Skip to main content
This file teaches you, the coding agent, how to add Trfyx analytics to the user’s website or app. Trfyx builds every report (pageviews, events, goals, funnels, users, revenue) from one tracking script, plus a payment-provider connection for revenue.

How you work with the user

Pasting this file is a request to set up Trfyx, not permission to change whatever you like. Work in this rhythm:
  1. Installing the tracking script needs no extra permission. It’s the one thing this file asks you to do. Ask for the website ID and domain if you don’t have them, then add the script (step 2).
  2. For anything else, suggest first and let the user choose. That covers custom events, identify, properties, revenue attribution and HTML attributes. Read the codebase and present one short list of what you could add (step 3). Implement only what the user picks.
  3. Once the user has picked, do the work without stopping at every step. Implement all the chosen items, then summarise what you changed. Check back mid-way only if you need something you don’t have, or the work turns out bigger or different from what you described. For example, if the checkout code needs restructuring, or client_reference_id is already used for something else.
  4. Keep changes to payment and sign-in code minimal and additive. Add the tracking lines; never change prices, amounts, currencies, redirect URLs or existing logic.
  5. Never do these unless the user asks: commit, push, deploy, install packages, edit .env files or secrets, or delete existing code.
  6. Don’t invent values. Never guess a website ID or domain. If the code doesn’t match what this file describes, say so instead of improvising.
  7. Respect privacy. Never send passwords, payment details or text the user typed into forms. Name and email go only in identify or the revenue email fallback.

Workflow

Step 1: look before you touch anything (read-only)

Find out, without editing:
  • The framework and where the root layout / <head> lives.
  • Whether Trfyx is already installed: search for script.js or script.cookieless.js with data-website-id, and for window.trfyx or trfyx(.
  • Where sign-up, sign-in, onboarding, key product actions, lead forms and checkout happen. Note the line where each one succeeds.
  • Which payment provider is used (Stripe, LemonSqueezy, other) and where the checkout session, link or payment is created on the server.
  • Where the signed-in user object is available on the client.

Step 2: install the script

If the script is missing, ask the user for (unless they already gave them):
  • their website ID, from Trfyx → Settings → Installation → Tracking script (the data-website-id in the snippet), and
  • the domain they registered in Trfyx.
Then add it to the site’s <head>:
  • Both data-website-id and data-domain are required; without either the script does nothing.
  • data-domain is the root domain (example.com, not www.example.com). The visitor cookie is set on it, so www., app. and blog. share one visitor.
  • If the user’s Trfyx install screen shows a different src (a custom tracking subdomain, or script.cookieless.js), use what their screen shows.
Where it goes: For Google Tag Manager or another tag manager, use a Custom HTML tag that injects the script:
Tracking is off on localhost, 127.0.0.1, *.local, *.localhost and file:// so development traffic doesn’t pollute real data. To test locally the user can temporarily add data-allow-localhost="true". Don’t leave it in committed code. After installing, tell the user what now works with no further code (see “What the script does on its own”), then go straight on to step 3.

Step 3: suggest what else to add

Using what you found in step 1, give the user one numbered list of what you could add, each with the file and one line on why. For example:
I can add:
  1. signup_completed event after sign-up succeeds (src/auth/signup.tsx:88): counts real sign-ups for a sign-up funnel.
  2. identify in the signed-in layout (app/(app)/layout.tsx): shows signed-in users by name in the Users report.
  3. Revenue attribution in the Stripe checkout route (app/api/checkout/route.ts:21): ties each payment to the visitor’s traffic source.
  4. data-trfyx-goal on the “Start free trial” buttons (components/hero.tsx): counts CTA clicks.
Which of these would you like? (“all” is fine.)
Wait for the answer. Implement only what the user picks.

Step 4: implement the chosen items

Follow the sections below. Do all the chosen items in one go, then list the files and lines you changed.

Step 5: verify, then tell the user what to set up in Trfyx

See “Verify it works” and “Goals and funnels”. Code alone creates no goals.

What the script does on its own

Once installed, with no other code: Session replay, heatmaps and web vitals are switched on per site in the Trfyx dashboard; no code is needed. Inputs are masked in replays by default. The script ignores automated browsers (Playwright, Puppeteer, Selenium, headless Chrome) and pages shown inside an iframe.

Custom events

  • The first argument is the event name. There is no "event" command: trfyx("event", "signup") is wrong.
  • The second argument is optional properties (see “Properties”).
  • The optional third argument is a callback that runs when the request finishes, e.g. to navigate away only after the event was sent: window.trfyx("download_clicked", {}, () => { location.href = url; }).
  • Use short snake_case names and keep them stable. Goals match the name exactly and case-sensitively.
  • Reserved names, never use them as events: payment, identify, props (these are commands), and pageview, heartbeat, engagement (these can’t be goals). Don’t reuse the automatic names form_submission, file_download and external_link for something else.

Always queue calls (important)

The script loads with defer, so window.trfyx may not exist when your code runs. Calling it then throws. Add this queue stub once; calls made before the script loads are replayed when it loads. Plain HTML, placed before any call (e.g. right above the Trfyx script tag):
In TypeScript/JavaScript apps, add one helper (e.g. lib/trfyx.ts) and call it everywhere instead of window.trfyx:

Send conversions on success, not on click (important)

A click or a form submit also happens when the request then fails (email already taken, validation error, card declined). Send the event after the server confirms success:
If success is only known on the server (webhook, background job, email verification link), suggest one of these instead:
  • send the event on the page the user lands on afterwards (/welcome, /verified), once per success; or
  • skip the event and use a pageview goal on that page (no code at all).

Events worth suggesting


Properties

Properties let reports and goals split an event, e.g. sign-ups by method. The browser checks properties before sending, and one bad property drops the whole event. The only sign is an error in the browser console. So:
  • Names: letters, digits, _ and - only, 1–32 characters. Names are lowercased (planType becomes plantype), so write snake_case.
  • At most 10 properties per call. An 11th drops the event.
  • Values become strings, are cut to 255 characters, and have < > ' " &, javascript:, data: and vbscript: removed. Send flat strings, numbers or booleans. Nested objects and arrays are not stored.
  • Don’t put personal data (email, phone, full name, address, free text the user typed) in event properties.

Properties on every event: props

This sends nothing by itself. It attaches these properties to every later event on the same page load, pageviews included. A later call merges in; it doesn’t replace. Call it early on each page load for facts true for the whole visit: plan, A/B variant, logged-in state. Same rules as above.

Identify signed-in users

  • Without user_id the call is ignored.
  • The visitor then appears by name in Trfyx’s Users report with all their sessions and events on that browser, including those from before they signed in.
  • Call it on every page load while the user is signed in (e.g. in the authenticated layout), and right after sign-up or sign-in succeeds.
  • Updating a user (new name, new plan): call identify again with the same user_id and the new values. The latest value of each trait wins.
  • Send flat values only, and keep it to about 25 traits.
  • Only identify with real data from your session or database, never from unconfirmed form input.
  • Nothing is needed on sign-out.
  • When you suggest identify, mention that it sends name and email, so the user can choose to send only user_id and a plan instead.
React example:
Render <TrfyxIdentify user={…} /> once in the signed-in layout.

Revenue attribution

Trfyx ties each payment to the visitor who made it, and so to their source, campaign, landing page and country. It takes two parts. Part 1 (the user, in Trfyx): connect the payment provider under Integrations → Stripe or LemonSqueezy. Trfyx creates the webhook itself. You don’t write webhook code for Trfyx. Part 2 (code, if the user picked it): pass the visitor’s ID into the checkout. Without it a payment can only be matched by weaker signals, or not at all.

Where the visitor ID comes from

The script stores it in a first-party cookie named df_visitor_id, set on the root domain from data-domain and kept for 365 days.
  • Backend on the same site (same root domain): read the cookie from the incoming request when creating the checkout.
  • Backend on a different domain: the browser won’t send the cookie there. Read it in the browser and include it in the request that creates the checkout:
  • The cookie can be missing (ad blocker, local development, cookieless mode). Never block or fail a checkout because it’s missing: pass the ID only when you have it.

Stripe Checkout

Pass the ID as client_reference_id. For subscriptions, also put it in the subscription’s metadata so each renewal invoice carries it.
When adding this to existing code:
  • Merge, don’t replace. If the code already sets metadata or subscription_data.metadata, add the df_visitor_id key to those objects. Never drop their existing keys or fields.
  • If client_reference_id is already used for something else (often the app’s own user ID), don’t overwrite it. Use the metadata keys only, and tell the user why.
  • Express / Node: req.cookies.df_visitor_id (with cookie-parser) or parse req.headers.cookie. In other stacks, read the request cookie the usual way.
What Trfyx reads from Stripe: Use the metadata key df_visitor_id. Customer metadata is not read. Stripe Payment Links (no backend): append the visitor ID to the link in the browser: https://buy.stripe.com/…?client_reference_id=<visitor id>. Renewals: once a subscription’s first payment is attributed, later renewals of the same subscription are attributed to the same visitor automatically.

LemonSqueezy

Put the visitor ID in the checkout’s custom data under df_visitor_id:
  • Checkout link or overlay: append ?checkout[custom][df_visitor_id]=<visitor id>, built in the browser from the cookie. If the link already has a ?, use &.
  • Checkouts API: in the create-checkout body, add it at attributes.checkout_data.custom.df_visitor_id, merged with any custom data already there.
Trfyx reads it back from the webhook’s meta.custom_data.

Fallback: match by email

If the visitor ID can’t be passed (e.g. a hosted checkout you can’t change), suggest sending the buyer’s email from the browser before redirecting to checkout, or on the success page:
Trfyx then matches a payment with that customer email to this visitor. It’s a lower-confidence match and sends the email to Trfyx, so present it as an option rather than a default. Things that don’t attribute revenue, so don’t suggest them for it: the identify email, success-page URLs, and custom events with an amount property.

Shopify

When the store is connected through Trfyx, the script saves the visitor ID on the cart and paid orders are attributed automatically. No code is needed.

No-code tracking with HTML attributes

Good for marketing pages, CMS content and buttons where adding JavaScript is awkward.

Click goals: data-trfyx-goal

  • Sends a custom event named by the attribute value when the element, or anything inside it, is clicked or activated with Enter or Space.
  • Each data-trfyx-goal-<name> attribute becomes a property. Dashes in <name> become underscores: data-trfyx-goal-cta-variant → cta_variant.
  • Works on elements added later too.
  • It counts the click, not success. For sign-ups or purchases prefer a JS event after success, or a pageview goal on the success page.

Element-seen events: data-trfyx-scroll

  • Sends a custom event when the element becomes visible.
  • threshold is the visible fraction from 0 to 1 (default 0.5). delay is the milliseconds it must stay visible (default 0).
  • Other data-trfyx-scroll-<name> attributes become properties. The event also carries scroll_percentage, threshold and delay.
  • It fires again each time the element leaves and re-enters the viewport.

Forms

Every valid form submit already sends form_submission. Suggest giving important forms an id (<form id="contact">) so a goal can single one out with the property condition id = contact. It fires on submit even if the server then rejects the form; for real leads, suggest a custom event on success.

Script tag options

Add these to the same <script> tag only when the user wants that behaviour. Each value is "true" unless shown otherwise. For single elements in replays, use the class rr-block (left out), rr-mask (text masked) or rr-ignore (input ignored), or the attributes data-replay-block, data-replay-mask, data-replay-ignore. Suggest these for sensitive areas such as account details or billing pages.

Cookieless mode

If the user turns on Cookieless mode in Trfyx → Settings → Installation, the snippet’s src becomes https://app.trfyx.com/script.cookieless.js. Tell them the trade-offs:
  • No cookies. Visitors are counted by a server-side hash that changes daily, so a returning person counts as a new visitor each day.
  • These don’t work in cookieless mode: trfyx("props", …), the callback argument, data-allowed-hostnames, data-hash-based-routing, session replay, heatmaps, web vitals, the Users report, and revenue attribution by visitor ID (there is no df_visitor_id cookie). Only the email fallback is left for revenue.
  • Custom events, identify, payment, data-trfyx-goal, data-trfyx-scroll and the automatic events work the same.

Verify it works

Walk the user through this, or do it yourself if you can run a browser:
  1. Open the site (deployed, or locally with data-allow-localhost="true") with DevTools → Network, filtered by collect.
  2. On page load, a POST …/api/collect with "type":"pageview" returns 202.
  3. Trigger the action. The request body should have "type":"custom" and extraData.eventName set to your event name, plus its properties.
  4. If nothing is sent, read the console’s Trfyx: messages:
    • “Tracking disabled on localhost” → add data-allow-localhost="true" while testing.
    • “Missing website ID or domain” → fix the script tag.
    • “Invalid property name” or “Maximum 10 custom parameters allowed” → fix the properties.
    • “Tracking disabled inside an iframe” → expected inside frames.
    • “automated client detected” → you’re in Playwright, Puppeteer or Selenium; test in a normal browser.
    • “tracking disabled via localStorage flag” → see below.
    • window.trfyx is not a function → add the queue stub.
  5. Ad blockers can block analytics, so test in a clean browser profile.
  6. The event appears in the Trfyx dashboard under Events within seconds.
For revenue, the user makes a test purchase (in test mode, with the provider connected in test mode), then checks that the Checkout Session or PaymentIntent in the provider’s dashboard shows the df_visitor_id / client_reference_id. Excluding the user’s own visits: in the browser console on their site, localStorage.setItem("df_ignore", "true"). Remove the key to undo. Office IPs and staging hostnames can be excluded in Trfyx → Settings → Exclusions.

Goals and funnels (in the Trfyx dashboard)

Events become conversions only when the user creates goals. You can’t do this from code, so finish by giving the user an exact list, for example:
Goals → New goal
  1. Trigger Pageview, page path /signup, display name “Visited sign-up”
  2. Trigger Custom event, event name signup_completed, display name “Signed up” (optional: Only count when method = email)
  3. Trigger Custom event, event name trial_started, display name “Started trial”
Funnels → New funnel: Visited sign-up → Signed up → Started trial
Facts to get right:
  • Goal triggers:
    • Pageview matches a page path.
    • Custom event matches the event name exactly, case-sensitively.
    • Scroll depth counts when a visitor scrolls a given page at least N % down (1–100).
  • Prefer a Pageview goal when the step has its own URL (/thank-you). Use a Custom event when it doesn’t, or when the URL can be reached without the action really happening.
  • Paths: a leading / is added automatically. * is a wildcard (/blog/*, /docs/*/install). Without * the match is exact. Scroll-depth goals take one exact path, no wildcard.
  • Only count when: up to 3 property = value conditions, all of which must match on the same event, exactly. It works on all three triggers. It only sees properties the site sends, not country, browser, referrer or UTM tags.
  • The automatic events form_submission, file_download and external_link can be custom-event goals, with drill-downs by page, file or URL.
  • Funnels: 2–8 steps, each a different goal, completed within 24 hours of the first step. With Allow other activity between steps on (usually right), other pages and events may happen in between. With it off, the steps must follow one another directly.
  • Goals are queries over events already recorded, so a goal created today also shows past conversions for events the site was already sending.

Quick reference

  • Visitor ID cookie for checkouts: df_visitor_id
  • Stripe: client_reference_id and/or metadata df_visitor_id
  • LemonSqueezy: checkout[custom][df_visitor_id]
Help: https://help.trfyx.com Reminder: install the script, suggest everything else, and implement only what the user picks — in one go, without asking at every step.