Skip to content

Product analytics and campaign attribution

Configure PostHog, Meta Pixel, Google Analytics, and UTM capture for marketing links and checkout attribution on OpenQuok.

4 min read

Connect your agent today

Draft from chat, review in your calendar, and publish only what you approve.

Start for $0

Overview

The web app records product analytics (usage and conversion events) and first-touch campaign data from query parameters. Attribution runs in the browser via UtmAttribution.svelte on the root layout; no extra page wiring is required for basic UTM storage.

This is separate from platform analytics (followers, impressions, per-post metrics from social providers). See Analytics APIs for channel and post insights.

Environment variables

Web (web/.env.production.local or development local)

VariablePurpose
VITE_PUBLIC_POSTHOG_KEYPostHog project token (phc_…)
VITE_PUBLIC_POSTHOG_HOSTPostHog ingest host (e.g. https://us.i.posthog.com)
VITE_PUBLIC_FACEBOOK_PIXELMeta Pixel ID for browser fbq
VITE_PUBLIC_GOOGLE_ANALYTICS_MEASUREMENT_IDGoogle Analytics measurement ID (G-…)
VITE_PUBLIC_STRIPE_PUBLISHABLE_KEYWhen empty, PostHog product events are skipped (self-hosted without billing)

Backend (backend/.env.production.local or development local)

VariablePurpose
FACEBOOK_PIXEL_IDSame numeric Pixel ID as VITE_PUBLIC_FACEBOOK_PIXEL
FACEBOOK_PIXEL_ACCESS_TOKENMeta Conversions API access token (server only; never expose in the web app)

More detail on Vite keys: Configuration — Vite (SvelteKit).

Where to get credentials

  • PostHogposthog.com → Project settings → Project token (phc_…) and API host.
  • Meta PixelMeta Events Manager → your Pixel → Pixel ID; Conversions APIGenerate access token (backend only).
  • Google Analytics — Google Analytics → Admin → Data streams → Measurement ID.

UTM and campaign capture

On every visit, the app can persist:

localStorage keySet whenMeaning
utmFirst matching query paramCampaign source string (first-touch)
landingUrlFirst visit onlyFull URL of the first page loaded
referrerFirst visit onlydocument.referrer on that first visit

Supported query parameters

The first non-empty value wins and is stored once (later visits do not overwrite utm):

  • utm_source
  • utm
  • ref

Newsletter

https://www.openquok.com/sign-up?utm_source=newsletter&utm_medium=email&utm_campaign=march

Stored value: newsletter (only utm_source is read into utm; medium and campaign are not saved unless you extend utm.ts).

Partner or affiliate

https://www.openquok.com/?ref=partner_acme

Stored value: partner_acme.

Paid social

https://www.openquok.com/?utm_source=meta&utm_campaign=prospecting

Stored value: meta.

Verify in the browser

  1. Open your site with a tagged URL, e.g. https://localhost:5173/?utm_source=test_dev.
  2. DevTools → ApplicationLocal Storage → your origin.
  3. Confirm utm, landingUrl, and referrer.
  4. Navigate to another route without query params — utm should still be test_dev.

Post-checkout return (check)

If the user lands with ?check=1 (or any truthy check query), the layout fires:

  • PostHog event purchase (when Stripe + PostHog are configured)
  • Meta conversion StartTrial (when Pixel + CAPI token are configured)

Example:

https://www.openquok.com/account/billing?check=1

Use this on return URLs after Stripe Checkout or trial confirmation flows you control.

Reading stored UTM in app code

Export readStoredUtm() from $lib/product-analytics returns the stored campaign string. You can attach it to checkout payloads or analytics event properties, for example:

import { readStoredUtm, fireProductEvent } from '$lib/product-analytics';

const utm = readStoredUtm();
fireProductEvent('initiate_checkout', { utm });

Event map (hosted product)

When billing is enabled (VITE_PUBLIC_STRIPE_PUBLISHABLE_KEY set), common events include:

User actionPostHog (approx.)Meta conversion
Sign-up successregisterCompleteRegistration
Subscribe clickInitiateCheckout
Checkout return with checkpurchaseStartTrial
Billing poll after checkoutpurchasePurchase
Channel connected (added query)channel_added
Search documentation
Find a docs page
Discord Support