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
Install tracking
Paste the workspace snippet from Connect > Web analytics and confirm the first pageview.
Tag campaigns
Create tracked links with UTMs so visits, conversions, and revenue keep their source.
Identify customers
Call Swell.identify after signup or login to merge anonymous and known activity.
Capture server events
Send backend-only milestones through the server helper when browser events cannot see them.
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>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.mdEvents 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.
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.
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" }
})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 },
})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.
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_abc123utm_sourceutm_mediumutm_campaignutm_contentsourcemediumcampaigncontentswell_link_idswell_post_idRevenue 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.
Consent And Capture Controls
Add `require_consent=true` when browser analytics, persistent identifiers, autocapture, Web Vitals, journey continuation, and replay should wait for consent. Add `autocapture=false` when you only want pageviews and explicit tracking calls. Mark sensitive areas with `data-swell-block`, `data-swell-ignore`, or `.swell-no-capture`.
<script async src="https://api.growonswell.com/swell.js?key=swl_pk_live_...&require_consent=true"></script>
<script>
window.Swell?.consent(true)
</script>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.
