Skip to main content

Web Pixel

Track web visits, engagement, and conversions from your LinkForty links with a single <script> tag. The web pixel is the browser-side counterpart to the mobile SDKs — where the SDKs attribute app installs, the pixel attributes web sessions and on-site conversions back to the link that drove them.

Cloud only

The web pixel is a LinkForty Cloud feature. It sends events to the Cloud ingestion endpoint and shows up in the Web Analytics dashboard. Self-hosted Core does not include the pixel ingestion pipeline.

What it does

  • Page views — every page load, including client-side route changes in single-page apps
  • Engagement — time on page, scroll depth, and bounce detection
  • Conversions — declarative (data-lf-event) or programmatic (window.LinkForty.track(...)), with optional revenue values
  • Link attribution — UTM parameters on the landing URL attribute the session (and its conversions) to the originating link
  • Autocapture (opt-in) — outbound clicks, file downloads, and form submissions with no code
  • Privacy-first — respects Do Not Track and Global Privacy Control, strips query strings and hashes from URLs, and never stores raw IP addresses

Install

Copy the snippet from your dashboard under Workspace Settings → Web Pixel (it's pre-filled with your workspace's site key), or build it yourself:

<script src="https://cdn.linkforty.com/pixel.js" data-workspace="ws_your_site_key" async></script>

Paste it into the <head> of every page you want to track. That's the entire installation — page views and engagement are tracked automatically.

Find your site key

Your site key (ws_…) lives under Workspace Settings → Web Pixel. It is public and safe to ship in your HTML. See Site key for rotation.

Configuration attributes

All configuration is read from data-* attributes on the script tag:

AttributeRequiredDefaultDescription
data-workspaceYesYour public workspace site key (ws_<32 hex chars>). Routes events to your workspace.
data-endpointNohttps://api.linkforty.com/api/pixel/v1/eventOverride the ingestion endpoint (e.g. when routing through a custom domain).
data-spaNotrueSingle-page-app route tracking. Set "false" to disable and send only the initial page view.
data-autocaptureNofalseSet "true" to auto-track outbound clicks, downloads, and form submits.
data-visitorsNofalseSet "true" to assign a persistent visitor ID for new-vs-returning analytics. Consent-sensitive — see Visitor ID.

Example with everything enabled:

<script src="https://cdn.linkforty.com/pixel.js"
data-workspace="ws_a1b2c3d4e5f6789abcdef0123456789a"
data-autocapture="true"
data-visitors="true"
async></script>

Tracking conversions

A conversion is any meaningful action you want to attribute back to a link — a signup, a purchase, a demo request. There are two ways to fire one.

Declarative (no JavaScript)

Add data-lf-event to any clickable element. When the element (or anything inside it) is clicked, the pixel fires a conversion with that name. Add data-lf-value for a numeric value such as revenue.

<button data-lf-event="signup_completed">Sign up</button>

<button data-lf-event="checkout_completed" data-lf-value="49.00">
Complete purchase
</button>
AttributeDescription
data-lf-eventConversion event name (max 255 chars). Fired on click.
data-lf-valueOptional numeric value (e.g. revenue). Non-numeric values are ignored.

Programmatic

Call window.LinkForty.track() from your own code — for conversions that happen after an async step (payment confirmation, server response):

window.LinkForty.track('checkout_completed', { revenue: 49.0, plan: 'pro' });
window.LinkForty.track(eventName: string, properties?: Record<string, unknown>): void
  • eventName — the conversion name (max 255 chars).
  • properties — optional flat object of custom data. Values may be primitives or arrays; objects are flattened out. Limited to 30 keys, 512 characters per string. Put revenue in a revenue property to have it counted in Web Analytics goals & revenue.

Tracking before the pixel loads

Because the script is async, it may not be ready when your code runs. Queue calls on window.LinkForty.q and the pixel replays them on load:

window.LinkForty = window.LinkForty || { q: [] };
window.LinkForty.q.push(['signup', { plan: 'pro' }]);

JavaScript API

The pixel exposes a small global on window.LinkForty:

MemberTypeDescription
track(name, properties?)functionFire a conversion event imperatively.
versionstringThe loaded pixel version (e.g. "1.2.3").
qarrayPre-load queue — push [name, properties] tuples here before the pixel loads.

Event types

Every event the pixel sends is one of three types:

TypeWhen it firesKey data
page_viewInitial load and each SPA route change (debounced, de-duplicated by URL)page title, language, URL, referrer, UTM
engagementOnce when the tab is hidden or the page unloadstime on page, scroll depth, bounce
conversiondata-lf-event clicks, track() calls, and autocaptureevent name, value, custom properties

All events also carry the page URL (query string and hash removed), the referrer (likewise trimmed), parsed UTM parameters, and the session/visitor IDs when available.

Transport

Events are sent with navigator.sendBeacon() (falling back to fetch with keepalive) as a POST to the ingestion endpoint with Content-Type: text/plain. This keeps the request CORS-safelisted (no preflight) and lets it complete even as the page unloads. The pixel adds no measurable load to your page and ships in well under 10 KB gzipped.

SPA route tracking

For single-page apps (React, Vue, Next.js, SvelteKit, etc.), the pixel automatically tracks client-side navigation. It patches the History API (pushState/replaceState) and listens for popstate (back/forward), then fires a page_view for each new route. Rapid transitions are debounced and duplicate URLs are ignored; hash-only changes are not counted as new views.

Session-scoped attribution (last-click). The UTM parameters on the page where a visitor lands are captured as the session origin and stored per-tab in sessionStorage. As the visitor navigates to UTM-less routes within your app, conversions still attribute to that originating link. If a later route carries new UTM parameters, it supersedes the origin (last-click wins).

Set data-spa="false" to turn this off — the pixel will then send only the initial page view.

Autocapture

Set data-autocapture="true" to track common interactions without writing any code. Three interaction types are captured, each sent as a conversion:

Event nameTriggerProperties
outbound_clickA link to a different host (http/https)href, host
file_downloadA link with a download attribute or a known file extension (pdf, zip, dmg, csv, docx, mp4, …)href, ext
form_submitAny <form> submissionformId, formName, action (when present)

Autocapture is off by default. Your own data-lf-event and track() conversions work the same whether or not autocapture is enabled.

Visitor ID (opt-in)

By default the pixel uses an in-memory, per-tab session ID only — there is no durable identifier. Set data-visitors="true" to additionally assign a persistent visitor ID (v_…) stored in localStorage. This powers new-vs-returning visitor analytics.

Consent required

A persistent visitor ID is the one durable identifier the pixel can create. Only enable data-visitors="true" where you have the appropriate user consent (GDPR/CCPA). If localStorage is unavailable (private browsing, blocked), no visitor ID is sent and tracking continues without it.

Privacy

The pixel is built to be privacy-respecting by default:

  • Do Not Track / GPC — if navigator.doNotTrack is "1"/"yes", or navigator.globalPrivacyControl is true, the pixel disables itself. track() becomes a no-op and no events are sent.
  • No raw IP storage — the ingestion endpoint uses the request IP only to derive coarse geolocation (country, region, city, timezone) and then discards it. The raw IP is never written to the database.
  • Trimmed URLs — query strings and hash fragments are stripped from the page URL and referrer before sending. (UTM parameters are parsed out separately for attribution.)
  • Bounded payloads — custom properties are flattened and capped (30 keys, 512 chars per value) so you can't accidentally exfiltrate large or nested PII.

Site key

Your site key identifies your workspace to the ingestion endpoint.

  • Format: ws_ followed by 32 hex characters (e.g. ws_a1b2c3d4e5f6789abcdef0123456789a).
  • Public: it lives in your page's HTML, so treat it as public — it grants the ability to send events to your workspace, nothing more.
  • Separate from your App Token. The mobile SDK appToken (at_…) and the web site key (ws_…) are independent. Rotating one never affects the other — important because an app token is baked into shipped app binaries (effectively non-rotatable), while a web key sits in scrapeable HTML and must be cheaply rotatable.

Rotating the site key

If your key is being abused (spam events from a scraped snippet), rotate it from the dashboard under Workspace Settings → Web Pixel → Rotate site key. Rotation is an owner-only action performed from your logged-in session. The old key stops working immediately, so update your <script> tag with the new key right after rotating.

Custom ingestion domain

By default the pixel sends events to LinkForty Cloud at api.linkforty.com. If you route LinkForty through a custom domain, point the pixel at that domain's ingestion endpoint with data-endpoint:

<script src="https://cdn.linkforty.com/pixel.js"
data-workspace="ws_your_site_key"
data-endpoint="https://app.yourcompany.com/api/pixel/v1/event"
async></script>

Troubleshooting

No events showing up

  1. Confirm data-workspace is your real site key (not the ws_… placeholder).
  2. Check the browser console/network tab for a POST to …/api/pixel/v1/event returning 204.
  3. A 204 with no row stored usually means an origin allowlist is configured under Workspace Settings and your domain isn't on it.
  4. Verify Do Not Track / GPC isn't enabled in your test browser — the pixel intentionally goes silent when it is.

Conversions not attributed to a link

  • Attribution is by UTM. Make sure the link a visitor arrives from carries utm_source/utm_medium/utm_campaign that match a link in your workspace. See UTM Parameters.

SPA route changes not tracked

  • Ensure data-spa isn't set to "false". The pixel relies on the History API; routers that do full page reloads will fire normal page_views instead.

Next steps