Acquisition Context Is Not the Latest Referrer

I maintain SensorFlow. This tutorial was drafted with AI assistance and checked with an offline synthetic fixture. It does not represent customer results or a built-in attribution feature.

A visitor arrives from a tagged newsletter, visits an internal page, and returns later from a partner link. If every navigation overwrites a single source property, a later signup can lose the original entry evidence. Keeping only the first value has the opposite limitation: it cannot answer what brought the visitor back.

Store separately named, explicitly scoped observations: an initial entry snapshot and a latest entry snapshot. Neither is automatically a person's lifetime acquisition source. Define identity scope, expiry, consent, and reset behavior before connecting these observations to conversion events.

Name the observation before choosing an attribution model

This example covers one synthetic browser scope and a supplied ordered sequence of external entry observations. It does not detect sessions, capture internal page navigation, persist browser storage, or merge devices. The first supplied observation stays first even if its source is unknown; a later known source does not rewrite history.

Google's campaign URL guide describes campaign parameters such as source, medium, and campaign. Those are explicit labels supplied with a link, not proof that a human saw an advertisement. This fixture records selected UTM labels; it does not reproduce GA4's acquisition dimensions or attribution engine.

The contract is intentionally small:

  • Input consists of approved entry observations, not every page view.
  • Retain only utm_source, utm_medium, and utm_campaign when nonblank.
  • Duplicate values for one selected parameter are rejected rather than silently choosing a winner.
  • Store no full landing URL, referrer path, search query, or fragment in the output.
  • Missing selected labels produce an unknown observation, never an invented direct fact.

An empty referrer may accompany direct navigation, as MDN explains, but that is not an exclusive explanation for missing evidence. Referrer-Policy can suppress or reduce referrer information. Do not infer a complete path, search keyword, or causal channel from data the browser did not provide.

Run a two-snapshot fixture

Save as acquisition.cjs and run node acquisition.cjs. It uses Node's standard library and synthetic .example URLs. No HTTP requests, cookies, storage writes, or SDK calls are made.

const assert = require('node:assert/strict');
function observe(urlText) {
  const url = new URL(urlText);
  assert(['http:', 'https:'].includes(url.protocol));
  const tags = {};
  for (const key of ['utm_source', 'utm_medium', 'utm_campaign']) {
    const values = url.searchParams.getAll(key);
    assert(values.length <= 1, 'ambiguous repeated campaign parameter');
    if (values.length && values[0].trim()) tags[key] = values[0];
  }
  return { state: Object.keys(tags).length ? 'tagged' : 'unknown', tags };
}
function snapshots(entries) {
  let initial = null, latest = null;
  for (const entry of entries) {
    const observation = observe(entry);
    if (initial === null) initial = observation;
    latest = observation;
  }
  return { initial, latest };
}
const newsletter = 'https://app.example/start?utm_source=newsletter&utm_medium=email';
const partner = 'https://app.example/start?utm_source=partner&utm_medium=referral';
const unknown = 'https://app.example/start';
const result = snapshots([newsletter, partner]);
assert.equal(result.initial.tags.utm_source, 'newsletter');
assert.equal(result.latest.tags.utm_source, 'partner');
assert.equal(snapshots([unknown, newsletter]).initial.state, 'unknown');
assert.equal(snapshots([newsletter, unknown]).latest.state, 'unknown');
assert.deepEqual(snapshots([]), { initial: null, latest: null });
assert.throws(() => observe('https://app.example/?utm_source=a&utm_source=b'));
assert.throws(() => observe('not a URL'));
assert.throws(() => observe('file:///tmp/example'));
assert.equal(observe('https://app.example/?utm_source=%20').state, 'unknown');
assert.deepEqual(observe('https://app.example/?email=synthetic#fragment').tags, {});
console.log(result);
console.log('PASS: 10 synthetic acquisition checks');

The newsletter remains the initial selected source, while the partner is the latest selected source. If the first observation has no tags, it stays unknown. A separate metric named “first known tagged entry” could make another choice, but it should not quietly replace this one.

The URLSearchParams API provides parsed parameter values and getAll. Duplicate detection is a local contract choice; it is not a claim that every analytics vendor rejects duplicates. The Node strict assertion API makes the acceptance checks fail explicitly.

Do not mistake an allowlist for a privacy guarantee

The output excludes arbitrary URL fields, but a selected UTM value could still contain a person's email, a secret, or another inappropriate value. An allowlist reduces accidental collection; it does not make the remaining data safe. Establish approved campaign vocabularies and review values before persistence. Never place credentials or personal identifiers in campaign links.

Likewise, the fixture accepts only HTTP and HTTPS URL syntax, but it does not validate trusted campaign ownership or prevent a user from editing tags. It is an analytics observation, not an authorization or billing signal.

Consent and retention policy are outside this function. For an actual application, decide whether collection is permitted, how long the snapshot is kept, what happens when consent changes, and when a browser scope resets. Do not deploy this as an unreviewed persistent tracking mechanism.

Attach conversion evidence without collapsing scope

At conversion time, keep the initial and latest contexts under different property names together with their observation scope and policy version. Preserve the occurrence time of each observation if you need time-based expiry; the minimal fixture intentionally omits timestamps and cannot implement lookback windows.

Do not merge two browsers because their campaign tags match. Do not merge two accounts because they used one browser. Identity resolution and entry-context storage are separate decisions; this example proves neither cross-device identity nor causal attribution.

For teams evaluating a self-hosted destination, the SensorFlow developer documentation describes setup and verification. Open-source deployment and the synthetic demo do not require a commercial license; receiving real Sensors Data SDK payloads requires activation. The code above is independent teaching code, not a claim that SensorFlow automatically captures or governs these snapshots.

The useful outcome is a pair of explainable observations, not a magically correct channel label. Keep the original evidence, the later evidence, and the rule that connects either one to a report visible to the people making decisions.

评论

此博客中的热门博文

Choosing an Event Analytics Stack Without a Feature Checklist

Event Tracking QA: Why Signup Funnel Numbers Disagree

Event Count Is Not User Count: Define the Metric First