1 · The anatomy of s
The first time you see an AppMeasurement implementation in the wild, it looks like a wall of
boilerplate. Calm down — there are really only three categories of thing inside the s
object:
| Category | Examples | You set these |
|---|---|---|
| Configuration | s.account, s.trackingServer, s.currencyCode | Once, when the page loads. |
| Data variables | s.pageName, s.eVar1, s.prop1, s.events | Before every hit. |
| Methods | s.t(), s.tl(), s.clearVars() | You call these. |
That's it. Configuration tells Adobe where to send the hit; data variables describe what happened; methods send the hit.
// Configuration (Launch's "Adobe Analytics" extension sets these for you)
s.account = "examplecorpprod";
s.trackingServer = "metrics.example.com";
s.trackingServerSecure = "smetrics.example.com";
s.visitorNamespace = "examplecorp";
s.currencyCode = "USD";
// Data — the page-view payload
s.pageName = "Product Detail: ASTRO-3000";
s.channel = "Telescopes";
s.eVar4 = "ASTRO-3000"; // product SKU
s.prop4 = "telescopes"; // product category
s.events = "prodView";
// Send it
s.t();
That five-line block is a working Adobe Analytics implementation. Everything else in this course is variation on these five lines.
2 · s.t() vs s.tl() — the only two methods that matter
AppMeasurement has many methods. Two of them carry 99% of the load. Master these and you can implement almost anything.
2.1 — s.t(): the page view
s.t() sends a page view hit. It is greedy: it sends every
variable currently set on s. Use it when a meaningful new page has loaded.
s.pageName = "Home";
s.channel = "Home";
s.eVar1 = "logged-in";
s.events = "event5";
s.t();
// → A page view hit, with pageName, channel, eVar1, events all sent.
Behaviour notes:
- Increments the page view counter.
- Sets
pageNamein the report suite — any subsequent link hits will attribute to it. - Clears
linkTrackVarsandlinkTrackEventsafter firing. - Other variables are not cleared. If you set
s.eVar1 = "x"on page A, it will fire again on page B unless you clear it. This is one of the great sources of bugs.
2.2 — s.tl(): the link hit
s.tl() sends a link hit. It is shy: by default it sends
almost nothing. You must explicitly whitelist the variables you want sent, using
s.linkTrackVars and s.linkTrackEvents.
s.linkTrackVars = "events,eVar15,prop15";
s.linkTrackEvents = "event10";
s.events = "event10";
s.eVar15 = "newsletter footer";
s.prop15 = "newsletter footer";
s.tl(true, "o", "Newsletter Signup");
// ↑ ↑ ↑
// | | └── linkName — appears in the Custom Links report
// | └────── linkType — 'o' (custom), 'd' (download), 'e' (exit)
// └───────────── linkObj — true (or `this`) tells AA not to delay the click
The signature is:
s.tl(linkObj, linkType, linkName, variableOverrides, doneAction);
| Argument | Meaning |
|---|---|
linkObj | true for fire-and-forget. Pass this only if you need the browser to wait (rare). |
linkType | 'o' custom, 'd' download, 'e' exit. The category in reports. |
linkName | Free-text label. Appears in the Custom Links / File Downloads / Exit Links report. |
variableOverrides | Object literal whose values override s's state for this one hit. Rarely used. |
doneAction | Callback function. Modern code uses Promises instead. |
If you set s.eVar15 = "x" and call s.tl(true, "o", "test") without
also setting s.linkTrackVars = "eVar15", the eVar will not be on the hit. This catches
everyone, including experienced implementers, at least once. Whitelist what you send.
3 · The variable families
3.1 — Standard variables
These have specific reports built around them. Adobe knows what they mean.
| Variable | Holds | Notes |
|---|---|---|
s.pageName | Page name for the Pages report | Set on every page view. Cap to ~100 chars. |
s.channel | Site section | Free-text. Reports as "Site Sections." |
s.server | Server / domain | Often the hostname. |
s.pageType | "errorPage" only. | Set this to count 404s as not-page-views. |
s.campaign | Tracking code (utm_*) | Persists as a marketing-channel touchpoint. |
s.events | Comma-separated event list | "event1,event3=4.99,purchase" |
s.products | Product string | See module 05. Multi-product is comma-separated. |
s.purchaseID | Order ID, for de-duping purchase events | Critical on order confirmation. |
s.transactionID | Order ID for Data Sources / offline join | Same value as purchaseID typically. |
s.zip, s.state | Customer location | Used in Profile reports. |
3.2 — Custom variables
Three families. Memorise these.
| Family | Range | Persistence | Used for |
|---|---|---|---|
eVar1–eVar250 * | up to 250 | Persists across hits per configured rules | Conversion attribution |
prop1–prop75 | up to 75 | Hit-level (resets every hit) | Traffic patterns, pathing |
event1–event1000 | up to 1000 | Hit-level | Counters and metric values |
hier1–hier5 | 5 only | Hit-level | Hierarchical site structure |
list1–list3 | 3 only | Hit-level, delimited multi-value | Multi-select facets, tags |
* Standard accounts get 75 eVars. Higher tiers get 100–250. Treat 75 as the practical ceiling.
Module 05 spends 75 minutes on these. For now, you need only this mental model: props describe traffic, eVars accumulate conversion credit, events count things.
3.3 — Hierarchy variables (hier)
A hierarchy variable holds a path: "Home,Telescopes,Astro 3000". Adobe parses the
separator and builds a drilldown report. Use them for site taxonomy.
s.hier1 = "Home,Telescopes,Astro 3000";
// └── separator default: comma
3.4 — List variables (list)
A list variable holds a delimited list of values, each of which Adobe treats as an independent
dimension value (so the metric is divided across them, not summed). Three exist:
list1, list2, list3.
// "User opened a product that had three categories"
s.list1 = "Telescopes,Astronomy,Optics";
// └── delimiter is configured in the report suite (default: comma)
Each value gets credit for the page view. Useful for tags, faceted search, or any one-to-many relationship between page and dimension.
3.5 — The products string
The products variable is the strangest piece of AppMeasurement. It packs a product list into a single semicolon-delimited string:
category;name;quantity;price;event-list;eVar-list
telescopes;ASTRO-3000;1;499.00;event3=1.00;eVar5=premium
Multiple products are separated by ,. So a two-product cart looks like:
s.products = "telescopes;ASTRO-3000;1;499.00,accessories;TRIPOD-X;1;79.99";
s.events = "scAdd";
Empty positions are still required:
s.products = ";ASTRO-3000;;;event3=2.50";
// ↑ ↑↑
// no category, no qty, no price — just SKU and a merchandising event
We will work through the products string in module 05 with the embedded sandbox. For now, remember its structure: category, name, quantity, price, events, eVars — separated by semicolons; products separated by commas.
4 · The wire format
When you call s.t(), AppMeasurement encodes your variables into a query string and
fires an HTTPS request. You will be inspecting this format for the rest of your career, so let's
look at it.
https://metrics.example.com/b/ss/examplecorpprod/1/JS-2.20.0/s47213094
?ns=examplecorp <-- visitorNamespace
&pageName=Product%20Detail
&ch=Telescopes <-- channel
&events=prodView,event3=1
&v4=ASTRO-3000 <-- eVar4
&c4=telescopes <-- prop4
&h1=Home,Telescopes <-- hier1
&l1=Telescopes,Optics <-- list1
&products=telescopes;ASTRO-3000;1;499.00
&g=https%3A%2F%2Fexample.com%2Fastro-3000.html
&r=https%3A%2F%2Fgoogle.com%2F
&s=1920x1080 <-- screen resolution
&mid=01234567890 <-- Marketing Cloud Visitor ID
&aid=ABCDEF12-34567890 <-- Analytics Visitor ID
The mapping you need to memorise is the short one:
| Param | Means |
|---|---|
v1, v2… | eVar1, eVar2… |
c1, c2… | prop1, prop2… |
h1–h5 | hier1–hier5 |
l1–l3 | list1–list3 |
v0 | s.campaign |
g | Current URL |
r | Referrer |
pe, pev2 | Link type, link name (link hits only) |
events | Event list (uncompressed name) |
products | Product string (uncompressed name) |
aid | Analytics ID (legacy visitor cookie) |
mid | Marketing Cloud / ECID |
Open a site you know uses Adobe Analytics. Open DevTools → Network → filter for
/b/ss/. Reload. Click the first matching request and read the URL with the table
above. You can now decode any AA hit you'll ever encounter. That single skill puts you ahead of
most people who claim Adobe Analytics on their CV.
5 · Account & config
These rarely change after initial setup, but knowing them prevents an entire class of "why is no data arriving" mysteries.
// The report suite(s) to write to
s.account = "examplecorpprod";
// Where to send the hit. Adobe issues this URL — get the right one or hits disappear.
s.trackingServer = "metrics.example.com";
s.trackingServerSecure = "smetrics.example.com";
// First-party cookie domain — Safari ITP demands a CNAME under your own domain
s.visitorNamespace = "examplecorp";
// Currency assumed for all monetary values
s.currencyCode = "USD";
// Character set of variable values
s.charSet = "UTF-8";
// "Outgoing link" determination — anything matching is treated as internal
s.linkInternalFilters = "javascript:,example.com";
Two configuration rules you should treat as commandments:
5.1 Use first-party cookies
Your trackingServer should be a CNAME that points to Adobe but lives under
your domain (e.g. metrics.example.com, not
examplecorp.sc.omtrdc.net). Otherwise Safari's Intelligent Tracking Prevention will
treat your visitor cookie as third-party and effectively expire it every seven days.
5.2 Set linkInternalFilters correctly
This is how Adobe decides which outbound clicks count as "exits." Anything in this comma-list is considered internal; anything else is an exit. Default is your own host — fine for single-domain sites, dangerously wrong for multi-domain estates.
6 · The full method list
| Method | Use |
|---|---|
s.t() | Fire a page view. |
s.tl(o, type, name) | Fire a link hit. |
s.clearVars() | Reset most variables to empty. Useful in SPAs (module 07). |
s.registerPreTrackCallback(fn) | Fire fn just before every hit. Use for last-minute mutation. |
s.registerPostTrackCallback(fn) | Fire fn after every hit. |
s.Util.getQueryParam(name) | Read query-string param from the current URL. |
s.Util.cookieRead(name) | Read a cookie by name. |
s.Util.cookieWrite(name, value, days) | Write a cookie. |
7 · The plugins you will see
For nearly two decades, Adobe shipped a library of helper functions called plugins. They aren't part of AppMeasurement's core — they're extra code you add via Launch. You'll see them everywhere.
| Plugin | Does |
|---|---|
getQueryParam | Read a URL query parameter. Used heavily for campaign tracking. |
getValOnce | Returns a value the first time it sees it, then empty until the cookie expires. De-dupes. |
getTimeParting | Returns day-of-week / hour-of-day strings. |
getPreviousValue | Stores last hit's value of a variable, returns the previous one. |
channelManager / getChannel | Determines marketing channel client-side (alternative to server-side rules). |
apl (append-to-list) | Appends a value to a delimited list if it isn't already present. Used for list variables. |
Adobe has been pushing teams to migrate plugin logic into Launch rules and data elements (where it is visible and version-controlled) rather than relying on hidden plugin code. Default to native Launch logic when starting fresh; respect plugin patterns when inheriting them.
8 · Live sandbox
Time to make this concrete. Below is a working mock of the s object. Edit the code,
click RUN, and the simulator decodes the hit you fired — every parameter, exactly
the way Adobe's collection server would receive it.
Goal: produce a page view hit with pageName, channel, an event, and
one eVar. Then add a link hit. Notice which variables travel with each.
Notice that the page-view hit carried eVar4 and prop4 automatically,
but the link hit only carried events and eVar4 — because that's what
linkTrackVars whitelisted. This is the single most important behavioural difference
between the two methods.
8.1 — Exercises
Add a second product
Change the page-view to carry a products string with two SKUs. Fire and
verify the encoding of the comma between products and the semicolon between fields.
Fire a download link
Send a link hit with type 'd' and name "PDF · Astro 3000 Spec Sheet".
Confirm the pe=lnk_d parameter on the wire.
Forget the whitelist
Remove the linkTrackVars line and re-run. Watch the link hit lose all your
custom variables. This is the bug you will diagnose dozens of times in your career.
Discover the leak
Set s.eVar1 = "premium" at the top, then fire two page views without
clearing. Watch eVar1 fire on both, even though you only meant it for the first. This is the
SPA bug from module 07.
You should now be able to: identify any variable from its querystring abbreviation, predict what
fires on a t() versus a tl(), build a multi-product products
string, and know why linkTrackVars exists. If any of that is unclear, replay the
relevant section in the sandbox.