Measure campaign journeys with Swell.

Install first-party analytics, preserve attribution, identify customers, connect trusted revenue, and configure consent-aware capture.

Tracking

Install Swell on your site, capture attribution, identify customers, and connect revenue without mixing public tracking keys with private API credentials.

Setup Path

1

Install tracking

Paste the workspace snippet from Connect > Web analytics and confirm the first pageview.

2

Tag campaigns

Create tracked links with UTMs so visits, conversions, and revenue keep their source.

3

Identify customers

Call Swell.identify after signup or login to merge anonymous and known activity.

4

Capture server events

Send backend-only milestones through the server helper when browser events cannot see them.

5

Connect revenue

Connect Stripe or another revenue source so paid outcomes are trusted server-side.

Install Tracking

Copy the snippet from Connect > Web analytics and place it on every public page you want to measure. The public key starts with `swl_pk_`; private `swl_sk_` API keys should never ship in browser code.

<script async src="https://api.growonswell.com/swell.js?key=swl_pk_live_..."></script>
Verify after deploy

Open your site, load a tracked page, then return to Analyze > Web. The install card should show traffic and the page should appear in recent events or top pages.

Read tracking.md
PageviewsLink clicksForm submitsWeb VitalsSession replay

Events And Identity

// Call after a client-side route change.
window.Swell?.page()

// Track business events that autocapture cannot infer.
window.Swell?.track("newsletter_signup", {
  placement: "footer",
  audience: "founders"
})

// Merge anonymous activity into a known customer journey.
window.Swell?.identify("jane@example.com", {
  full_name: "Jane Doe",
  lifecycle_stage: "trial"
})

// Record a non-revenue conversion milestone.
window.Swell?.conversion("trial_started", {
  plan: "growth"
})

Use explicit calls for milestones

Call `Swell.page()` after SPA route changes, `Swell.track()` for product actions, `Swell.identify()` when an email is known, and `Swell.conversion()` for non-revenue milestones like trials or demo requests.

Keep event names readable and payloads small. Do not send passwords, tokens, raw payment details, or sensitive personal data.

Server-Side Capture

Use server-side capture for backend-only signups, lead forms, activation milestones, invite acceptance, and other events that happen after an API request. Keep `swell.js` installed for pageviews, attribution, Web Vitals, autocapture, and replay.

Node / Next.js
const SWELL_TRACKING_KEY = process.env.SWELL_PUBLIC_TRACKING_KEY
const SWELL_TRACK_URL = "https://api.growonswell.com/api/v1/growth/track"

export async function captureSwell(event) {
  if (!SWELL_TRACKING_KEY) return

  const response = await fetch(SWELL_TRACK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      tracking_key: SWELL_TRACKING_KEY,
      event_type: event.event_type ?? "track",
      event_name: event.event_name,
      client_event_id: event.client_event_id ?? crypto.randomUUID(),
      email: event.email,
      full_name: event.full_name,
      path: event.path,
      url: event.url,
      referrer: event.referrer,
      source: event.source,
      medium: event.medium,
      campaign: event.campaign,
      content: event.content,
      properties: event.properties ?? {}
    })
  })

  if (response.status === 429 || response.status === 503) return
  if (!response.ok) throw new Error(`Swell capture failed: ${response.status}`)
}

await captureSwell({
  event_type: "conversion",
  event_name: "trial_started",
  email: user.email,
  full_name: user.name,
  path: "/signup",
  properties: { plan: "growth", source_system: "app_server" }
})
Python / FastAPI
import os
import uuid
import httpx

SWELL_TRACKING_KEY = os.getenv("SWELL_PUBLIC_TRACKING_KEY")
SWELL_TRACK_URL = "https://api.growonswell.com/api/v1/growth/track"

async def capture_swell(event: dict) -> None:
    if not SWELL_TRACKING_KEY:
        return

    payload = {
        "tracking_key": SWELL_TRACKING_KEY,
        "event_type": event.get("event_type", "track"),
        "event_name": event["event_name"],
        "client_event_id": event.get("client_event_id", str(uuid.uuid4())),
        "email": event.get("email"),
        "full_name": event.get("full_name"),
        "path": event.get("path"),
        "url": event.get("url"),
        "referrer": event.get("referrer"),
        "source": event.get("source"),
        "medium": event.get("medium"),
        "campaign": event.get("campaign"),
        "content": event.get("content"),
        "properties": event.get("properties", {}),
    }

    async with httpx.AsyncClient(timeout=3.0) as client:
        response = await client.post(SWELL_TRACK_URL, json=payload)
        if response.status_code in (429, 503):
            return
        response.raise_for_status()

await capture_swell({
    "event_type": "conversion",
    "event_name": "demo_requested",
    "email": lead.email,
    "full_name": lead.name,
    "path": "/demo",
    "properties": { "company_size": lead.company_size },
})
Use the public tracking key

Server capture still uses the workspace `swl_pk_` key from Connect > Web analytics. Include `client_event_id` for retried jobs so Swell can deduplicate events.

Keep revenue trusted

Do not send `event_type: "revenue"` or `value_cents` through public tracking. Use Stripe webhooks, connected revenue providers, or verified imports for paid outcomes.

Campaign Attribution

Create tracked links in Create > Links or through `POST /v1/links`. Swell appends UTMs and `swell_link_id`, then captures those values as first-touch and last-touch context when `swell.js` is installed on the destination site.

https://example.com/pricing?utm_source=linkedin&utm_medium=organic&utm_campaign=summer_launch&utm_content=carousel&swell_link_id=tl_abc123
Recognized fields
utm_sourceutm_mediumutm_campaignutm_contentsourcemediumcampaigncontentswell_link_idswell_post_id

Revenue And Paid Ads

Conversions

Browser conversions mark milestones and carry attribution context, but they are not trusted revenue.

Revenue

Use connected Stripe accounts, signed provider webhooks, or server-side imports to create revenue rows that roll into People contacts and workspace users when metadata resolves.

Paid ads

Paid ad connectors are read-only and join spend to visits, conversions, and revenue through UTMs and click markers.

Bot Tracking

AI assistants and search crawlers usually do not run browser analytics. Install the server, Vercel Edge, Cloudflare Worker, or log-forwarder snippet from Analyze > Bots so crawler requests are recorded before your page is returned.

await fetch("https://api.growonswell.com/api/v1/growth/bots/track", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    tracking_key: "swl_pk_live_...",
    url: request.url,
    method: request.method,
    user_agent: request.headers.get("user-agent") || "",
    ip: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || ""
  })
})

Verified IP means the crawler IP matched a published OpenAI or Google range. User agent means Swell recognized the bot string but the provider does not expose a stable machine-readable IP range to verify.

Swell stores a salted IP hash, bot family, category, path, referrer, status, and timestamp. Raw IPs are not returned in analytics responses.