AbsoluteJS

Attribution

@absolutejs/attributionv0.1.0betaCommerce & Growth

Privacy-aware click-ID capture, allowlisted link decoration, resilient Google tag loading, and consent-aware server conversion delivery.

@absolutejs/attribution keeps paid-click identity alive without turning attribution into a second analytics database. It captures validated Google click identifiers, stores only the identifiers and capture time, decorates links only for explicitly trusted origins, and provides a resilient Google tag controller. A separate server helper sends consent-aware conversions to Google Data Manager so applications can pair browser measurement with a retryable, idempotent backend job.

#Installation

BASH
bun add @absolutejs/attribution

#Capabilities

Overview

Privacy-aware attribution primitives for web applications:

capture gclid, gbraid, and wbraid without persisting full landing URLs;

forward identifiers only to explicitly allowlisted owned origins;

Show 6 more

load the Google tag through a retrying idle → loading → ready/failed state

machine;

keep consent and conversion commands queued while the tag recovers;

emit identifier-free load telemetry; and

supplement browser tag conversions through Google Data Manager using the same

transaction ID for deduplication.

Durable Google conversion supplement

The package never sends full landing URLs or click identifiers through its telemetry callback. Server delivery requires an explicit access-token provider, destination, consent state, and click identifier.

Minimal click-ID storage

Capture gclid, gbraid, and wbraid with strict validation and a configurable age limit. Full landing URLs are never stored.

Owned-origin forwarding

Decorate outbound links only when their exact origin is in the caller-provided allowlist.

Recoverable tag loading

The Google tag controller exposes idle, loading, ready, waiting-online, failed, and closed states with bounded retries.

Consent-first measurement

Default and updated Consent Mode commands are queued before tag configuration and remain available while the script recovers.

Conversion deduplication seam

Track conversions with value, currency, destination, completion timeout, and a transaction ID that can also identify server delivery.

Server conversion supplement

Send a validated, consent-aware conversion to Google Data Manager using an injected token provider and fetch implementation.

#Attribution lifecycle

Explore the handoff from a paid landing page to browser and server conversion delivery. Each boundary is explicit, so attribution does not leak into unrelated links or telemetry.

  1. Capture
    Read gclid, gbraid, and wbraid from the landing URL. Invalid characters, empty values, and values over 512 characters are rejected.
  2. Minimize
    Save only the validated identifiers and capturedAt timestamp in sessionStorage. The full landing URL is never persisted.
  3. Decorate
    Forward identifiers only when the destination origin appears in your allowlist. Every other URL is returned undecorated.
  4. Measure
    Start the Google tag after hydration. Consent commands and conversions queue while bounded retries recover from temporary loading failures.
  5. Supplement
    Enqueue a backend job with the same transaction ID, consent state, and click identifier. Google can deduplicate browser and server delivery.

#Choose a delivery strategy

Choose the smallest delivery surface that meets your reliability requirements. Browser and server delivery are complementary, not competing implementations.

Best for: Lightweight funnels where best-effort browser measurement is sufficient.

Tradeoffs: Simple and immediate, but extensions, privacy controls, offline clients, and upstream script failures can block delivery.

Requirements
  • A Google Ads tag ID
  • An explicit consent state
  • Start the controller after framework hydration

#Privacy boundaries

The package keeps each data surface narrow. Use this matrix when reviewing privacy behavior or deciding what application state to retain.

OptionClick IDsFull landing URLBoundary
Stored snapshotYesNeversessionStorage + expiry
Decorated linkYesNoExplicitly allowlisted origins only
Load telemetryOnly a booleanNeverAttempt, state, recovery
Data Manager requestYesNeverExplicit server destination

Outcomes

What you can build

Overview

Privacy-aware attribution primitives for web applications:

Durable Google conversion supplement

The package never sends full landing URLs or click identifiers through its telemetry callback. Server delivery requires an explicit access-token provider, destination, consent state, and click identifier.

Hardening checklist

Production guidance

Durable Google conversion supplementThe package never sends full landing URLs or click identifiers through its telemetry callback. Server delivery requires an explicit access-token provider, destination, consent state, and click identifier.

Follow in order

Troubleshooting path

1
Trace from the first failed boundary
Reproduce the smallest canonical @absolutejs/attribution example, confirm the supported entry point and version in the API explorer, then inspect the first boundary that did not produce its documented result.

#Browser attribution

Partial snippet

Working example for Browser attribution.

TS
import { createAttributionStore } from "@absolutejs/attribution";
import { createGoogleAdsTag } from "@absolutejs/attribution/google-ads";

const attribution = createAttributionStore();
attribution.capture();

const google = createGoogleAdsTag({
  attribution,
  consent: {
    adPersonalization: "denied",
    adStorage: "denied",
    adUserData: "denied",
    analyticsStorage: "denied",
  },
  id: "AW-123",
  onTelemetry: (event) => console.info(event),
});

// Start after framework hydration/mount.
google.start();

const qualificationUrl = attribution.decorate("https://qualify.example.com", [
  "https://qualify.example.com",
]);

#Durable Google conversion supplement

Partial snippet

Working example for Durable Google conversion supplement.

TS
import { sendGoogleAdsDataManagerConversion } from "@absolutejs/attribution/google-ads";

await sendGoogleAdsDataManagerConversion(
  {
    accessToken: getGoogleAccessToken,
    accountId: process.env.GOOGLE_ADS_ACCOUNT_ID!,
    conversionActionId: process.env.GOOGLE_ADS_CONVERSION_ACTION_ID!,
  },
  {
    consent: { adPersonalization: "denied", adUserData: "denied" },
    eventTimestamp: new Date().toISOString(),
    identifiers: { gclid },
    transactionId: paymentTransactionId,
  },
);

#Browser setup

Partial snippet

Capture a paid-click identifier, establish denied-by-default consent, and start the retrying Google tag after hydration.

TS
import { createAttributionStore } from '@absolutejs/attribution';
import { createGoogleAdsTag } from '@absolutejs/attribution/google-ads';

const attribution = createAttributionStore();
attribution.capture();

const google = createGoogleAdsTag({
	attribution,
	consent: {
		adPersonalization: 'denied',
		adStorage: 'denied',
		adUserData: 'denied',
		analyticsStorage: 'denied'
	},
	id: 'AW-123456789',
	onTelemetry: ({ attempt, event, recovered }) => {
		observeTagLoad({ attempt, event, recovered });
	}
});

// Call after framework hydration or mount.
google.start();
Partial snippet

Decorate an owned checkout origin, update consent from your consent UI, and use the order ID as the browser conversion transaction ID.

TS
const checkoutUrl = attribution.decorate(
	'https://checkout.example.com/upgrade',
	['https://checkout.example.com']
);

google.updateConsent({
	adPersonalization: 'denied',
	adStorage: 'granted',
	adUserData: 'granted',
	analyticsStorage: 'granted'
});

google.trackConversion({
	currency: 'USD',
	sendTo: 'AW-123456789/purchase',
	transactionId: order.id,
	value: order.total
});

#Server supplement

Partial snippet

Run Data Manager delivery inside your durable queue. Reuse the browser transaction ID so Google can deduplicate both paths.

TS
import { sendGoogleAdsDataManagerConversion } from
	'@absolutejs/attribution/google-ads';

const conversion = {
	consent: {
		adPersonalization: 'denied',
		adUserData: 'granted'
	},
	currency: 'USD',
	eventTimestamp: order.paidAt.toISOString(),
	identifiers: order.attributionIdentifiers,
	transactionId: order.id,
	conversionValue: order.total
} as const;

await conversionQueue.add('google-ads-conversion', conversion);

// In the durable worker:
await sendGoogleAdsDataManagerConversion(
	{
		accessToken: getGoogleAccessToken,
		accountId: env.GOOGLE_ADS_ACCOUNT_ID,
		conversionActionId: env.GOOGLE_ADS_CONVERSION_ACTION_ID
	},
	conversion
);
Durability belongs to your job system
The Data Manager helper performs one authenticated request. Put it behind your existing durable queue and retry policy; the package deliberately does not hide delivery state in browser storage or an in-memory server retry loop.
Forward deliberately
An allowlist is required for link decoration. Passing an untrusted origin leaves the target unchanged, preventing click identifiers from following arbitrary outbound links.

#API reference

Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.

11 symbols
GOOGLE_CLICK_ID_PARAMETERSvaluePermalink
TS
const GOOGLE_CLICK_ID_PARAMETERS: readonly ["gclid", "gbraid", "wbraid"];
Exported from @absolutejs/attribution

Current package surface

What ships today

@absolutejs/attributionv0.1.0 · betaDev ToolsnpmSource
2entry points25symbols

Import surface · click to copy