RTN Docs

Web SDK · v0.6.0

Attribution

How RTN classifies campaign and platform touches, and how to read local or server-confirmed attribution.

The SDK records where every visit came from and sends context.attribution = { first_touch, last_touch } whenever the server may not have it yet. No extra code is required; tag your links correctly.

The touches travel on:

  • the first event of every session,
  • order_completed, checkout_started and signed_up, so conversions always carry their campaign,
  • every event after a new touch until the ingest has accepted one of them (a failed or retried request does not count).

Other events stay small; the server stores the touches once per anonymous ID and waitForAttribution() reads them back.

  • first_touch is the first recorded visit of this browser, the web equivalent of an install referrer. It is never overwritten.
  • last_touch is the most recent non-direct visit. A direct visit never replaces it. A reload or back navigation in the same session keeps its time; returning through the same link after the session ended (30 minutes of inactivity) moves at, so attribution windows count from the latest click.

In the browser, each touch holds the raw signals: UTM values (source, medium, campaign, term, content), the ad click ID (click_id, click_id_type), referrer_domain, landing_path, and at. The ingest edge classifies them into platform and channel (and fills source and medium when absent), so classification rules can improve without an SDK upgrade.

SourceExampleEdge result platform / channel
Google Adsautomatic gclid + ?utm_campaign=black_fridaygoogle / paid_search
Google organicreferrer google.comgoogle / organic_search
Instagram ad?utm_source=instagram&utm_medium=paid_social&utm_campaign=winter (+ fbclid)instagram / paid_social
Facebook / Instagram organiconly fbclid, referrer l.facebook.comfacebook / organic_social
TikTok adautomatic ttclidtiktok / paid_social
Microsoft Ads / Yandex Directmsclkid / yclidmicrosoft / yandex, paid_search
Newsletter?utm_source=newsletter&utm_medium=email&utm_campaign=octoberemail / email
RTN message?utm_source=rtn&utm_medium=sms&utm_campaign=cart_reminder&rtn_cid=<message_id>rtn / sms
Affiliate?utm_source=partner&utm_medium=affiliatepartner / affiliate
Other sitereferrer blog.example.orgblog.example.org / referral
Directno referrer or parametersdirect / direct

Returning from a payment page (payment providers, bank 3-D Secure pages) does not overwrite the campaign. Add your own provider with ignoredReferrers:

rtn.init('WRITE_KEY', { ignoredReferrers: ['pay.example-psp.com'] });

Store promotions on orders

Attach the promotion applied at checkout to orderCompleted:

rtn.orderCompleted({
  order_id: 'ORD-1001',
  products: [/* ... */],
  value: 2339.82,
  discount: 259.98,
  coupon: 'WINTER10',
  promotion_id: 'winter_2026',
  promotion_name: 'Winter 10%',
});

Read attribution in the browser

getAttribution() returns the local snapshot, or null without consent:

const local = rtn.getAttribution();
console.log(local?.last_touch.source);

waitForAttribution() asks the ingest service for the server-confirmed attribution. It never blocks event collection: if no answer arrives within timeoutMs (default 2000 ms), it resolves with the local snapshot.

const attribution = await rtn.waitForAttribution({ timeoutMs: 2000 });

Connect backend records to the visitor

To link a server-side record, such as an order created by your backend, to the browser visitor, send the anonymous ID with your form. getAnonymousId() returns null without consent.

rtn.ready(() => {
  document.querySelector('#checkout-form input[name=rtn_anonymous_id]').value =
    rtn.getAnonymousId() ?? '';
});

This is an integration input only. The current platform does not verify orders or refunds on the server, and order_completed remains a client-side signal marked estimated: true.

On this page