Overview
Every website you track with Metric Sensei has one tag. Depending on the services turned on for the website it runs
Analytics (page views, engagement, events, revenue) and/or Attribution (visitor IDs, marketing
touches, form filling). Your website talks to both through one global function, msa(...), much like
Google's gtag(...).
This page lists everything a website admin or developer can call, what each call takes and what it gives back. Calls for a service that isn't on for the website are ignored, so the same code is safe on every page.
| You want to | Call | Service |
|---|---|---|
| Count something that happened (sign-up, purchase, video play) | msa('event', name, params) | Analytics |
| Add the same parameters to every event | msa('set', params) | Analytics |
| Count a page view the URL doesn't show | msa('pageview', {path}) | Analytics |
| Read the visit's info | msa('analytics') | Analytics |
| Read A/B test variants and feature flags | msa('variant', key), msa('flag', key) | Analytics add-on |
| Read the visitor's IDs and marketing touches | msa('ready', fn), msa('get') | Attribution |
| Put the source, campaign and IDs into your forms | ms_source, ms_campaign ... fields | Attribution |
| Link the visitor to a known person | msa('identify', emailOrUserId) | Attribution |
| Pass your cookie banner's answer | msa('consent', {analytics, ads}) | Both |
| Show a visitor their ID for a privacy request, or clear it | msa('privacy', fn), msa('privacy', 'forget') | Attribution |
Install the tag
Copy your website's snippet from the admin page (the client's
Websites & Apps, then the website, then Install) and put it high in the <head> of
every page. It looks like this, with your site ID in place of MS-XXXXXXXX:
<script>(function(w,d,i){w.msa=w.msa||function(){(w.msa.q=w.msa.q||[]).push(arguments)};
var s=d.createElement('script');s.async=1;s.src='https://cdn.metricsensei.com/s/'+i+'.js';
d.head.appendChild(s)})(window,document,'MS-XXXXXXXX');</script>
The WordPress plugin, the Shopify app and the HubSpot and Salesforce installs add the same snippet for you. With
first-party mode the script is served from your own domain instead of cdn.metricsensei.com; nothing else changes.
The tag only runs on the hosts listed for the website (its domain and the subdomains you chose), and never in automated browsers unless the website's debug switch is on.
How msa(...) calls work
The snippet defines msa at once as a small queue, then loads the real tag in the background. Calls made
before the tag has loaded are kept and run, in order, as soon as it loads. So you can call msa(...) anywhere,
any time, without waiting.
- Return values only exist once the tag has loaded. A call that returns something
(
msa('analytics'),msa('get'),msa('variant', key),msa('flag', key)) returnsundefinedwhile it is still queued. Use the callback forms when you need the answer early:msa('ready', fn)andmsa('analytics', fn). - Unknown commands are ignored. A command for a service that isn't on waits in the queue and is never sent.
- Calls never throw. A bad argument is left out, and with debug on a warning is written to the browser console.
Quick start: send an event
// A sign-up, with two parameters
msa('event', 'sign_up', { method: 'Google', plan: 'pro' });
// A purchase, GA4 style: value + currency count as revenue
msa('event', 'purchase', {
transaction_id: 'T-10293',
value: 59.90,
currency: 'USD',
coupon: 'FALL10',
items: [
{ item_id: 'SKU-1', item_name: 'Mug', price: 19.95, quantity: 2 },
{ item_id: 'SKU-7', item_name: 'Tea', price: 20.00, quantity: 1 }
]
});
// From a button
document.querySelector('#demo').addEventListener('click', () => msa('event', 'demo_request'));
Events show up in the client's Analytics page under Goals & events (and in Realtime within a minute). To count an event as a conversion, add an event goal with the same name in the website's Analytics settings.
msa('event', name, params)
Sends one named event for the current page and visit.
| Argument | Type | Notes |
|---|---|---|
name | string | Required. 1 to 60 characters: letters, digits, spaces, _ . : / & ( ) ' # + -. Case matters
(Signup and signup are two events). Names starting with $ are reserved for Metric Sensei and are ignored.
GA4's snake_case names (sign_up, generate_lead, purchase) work as they are. |
params | object | Optional. See parameters and limits. |
Returns true when the event was queued to send, false when it wasn't (bad name, a
$ name, a transaction_id already sent from this browser, or a page excluded in the website's settings).
While the tag is still loading the call is queued and returns undefined.
When it is sent. Events are batched with page views and sent within 5 seconds, after 10 events, or when the
page is hidden or closed (using navigator.sendBeacon, which survives leaving the page). You don't need to wait before navigating.
event_callback: run code after the event is handed over
As in GA4, a function in event_callback is called once the event has been handed to the browser to send.
It gets true or false (whether the event was sent) and is always called, also when the event was left out.
form.addEventListener('submit', (e) => {
e.preventDefault();
msa('event', 'generate_lead', { form: 'contact', event_callback: () => form.submit() });
});
If the tag can't load (an ad blocker, or a call before the snippet), the callback is never called. As with GA4,
add your own fallback timer when the callback continues something the visitor is waiting for:
setTimeout(go, 1000).
Parameters and limits
| What | Limit | What happens over it |
|---|---|---|
| Parameters per event | 25 (same as GA4), including the ones from msa('set') | The rest are left out |
| Parameter name | 40 characters | Cut to 40 |
| Text value | 200 characters (GA4: 100) | Cut to 200 |
| Value types | text, number, true/false | Objects, arrays (other than items), functions, NaN, empty text and null are left out |
items | 10 items | The rest are left out |
| Event name | 60 characters | Cut to 60; other characters make the event invalid |
| Events per page batch | 50 | Sent in the next batch |
Text, numbers and true/false
- Text values are counted per value: the events report shows, for each event, how often each parameter value
occurred (
plan = pro: 120). Each website keeps its 300 most common event/parameter/value combinations per day. - Numbers are added up: the report shows, per event and parameter, how many events had it, the total and the
average (
duration_s: 340 events, total 51,200, average 150.6). Send IDs as text ('10293', not10293) so they are counted rather than added up. - true / false are kept as the text
"true"and"false".
Names with a meaning
| Parameter | Meaning |
|---|---|
value or revenue | Money from this event (a positive number below 10,000,000). Counted as revenue. revenue wins if both are sent. |
currency | Three-letter ISO code (USD, EUR). Revenue only counts in the website's report currency (set in its Analytics settings, USD by default); other currencies are kept in the raw data but not added to the revenue report. |
transaction_id | Sent once per browser: the same ID again within 24 hours is not sent (stops double counting when a thank-you page is reloaded). Not shown as a value breakdown. |
items | Array of products, see ecommerce. |
event_callback | Function called after sending, see above. Never sent. |
Revenue and ecommerce
Any event can carry revenue. GA4's ecommerce events work unchanged:
msa('event', 'add_to_cart', { currency: 'USD', value: 19.95,
items: [{ item_id: 'SKU-1', item_name: 'Mug', item_category: 'Kitchen', price: 19.95, quantity: 1 }] });
msa('event', 'purchase', { transaction_id: 'T-10293', value: 59.90, currency: 'USD', shipping: 4.99, tax: 4.10,
items: [{ item_id: 'SKU-1', item_name: 'Mug', price: 19.95, quantity: 2 }] });
| Item field | Type | Notes |
|---|---|---|
item_id | text or number | An item needs an ID or a name, else it is left out |
item_name | text | Shown in the items report (the ID when there is no name) |
item_category, item_brand, item_variant | text | 100 characters each; kept in the raw data |
price | number | Per unit |
quantity | whole number | 1 when left out; 1 to 10,000 |
The events report shows per event and item: events, quantity and item revenue (price × quantity). Event revenue
(value) drives the Revenue report by channel, campaign, entry page and event, with revenue per visit and ROAS.
Revenue is not worked out from items: send value.
On Shopify and WooCommerce the store's own orders also come in from the store connection; send browser purchase events only if you want them in Analytics too.
msa('set', params)
Default parameters added to every later event on this page (GA4's gtag('set', ...)). The event's own parameters win when a
name is in both. null removes a default. Defaults last until the page is left (in a single-page app, until a full reload).
msa('set', { logged_in: true, plan: 'pro' });
msa('event', 'export'); // sent with logged_in and plan
msa('set', { plan: null }); // stop sending plan
Returns nothing. Defaults count towards the 25 parameters. They are also added to the events the tag sends by itself.
msa('pageview', options)
Page views are counted for you, including in single-page apps that change the address with history.pushState
(and #/ routes when hash routes is on in the website's settings). Call this only for screens whose address doesn't change:
steps of a checkout, tabs, modals that act as pages.
msa('pageview', { path: '/checkout/shipping' });
| Option | Notes |
|---|---|
path | Must start with /. Anything after ? or # is removed. Without it, the current address is used. |
The same path twice in a row counts once. Later events belong to this path until the next page view.
msa('analytics', callback)
Returns the visit's Analytics info, and also calls callback with it when you pass one (use the callback if the tag may not
have loaded yet).
msa('analytics', (info) => console.log(info.visitId, info.consented));
| Field | Type | Meaning |
|---|---|---|
siteId | string | The website's ID (MS-...) |
visitId | string | This visit's random ID (a visit ends after 30 minutes without activity, or when a new campaign or outside site brings the visitor back) |
path | string | The page path events are counted on now |
version | string | The Analytics tag version |
consented | boolean | Whether the visitor allows analytics (see consent). When false, visits are still counted, anonymously. |
defaults | object | The parameters set with msa('set') |
msa('variant', key) and msa('flag', key)
With the A/B tests & feature flags add-on. Experiments and flags are set up in the client's Analytics page (or suggested by Website Optimizer).
| Call | Returns |
|---|---|
msa('variant', 'hero-test') | The visitor's variant key (for example "b"), or "" when the experiment isn't running on this page. The first answer per page records that the visitor saw the experiment. |
msa('flag', 'new-pricing') | true or false for this visitor |
Running variants also add a class to <html> right away, msa-exp-<experiment>-<variant>, so CSS can switch
content without waiting for JavaScript. Variants are stable per visitor.
Events sent for you
You don't need code for these; each can be turned off in the website's Analytics settings. Don't send events with these names yourself.
| Event | Parameters | Setting |
|---|---|---|
Outbound Link: Click | url, domain, text, page | Outbound links |
File Download | url, file, text, page | Downloads |
Form: Submit | form (id or name), path, action, button; never field values | Forms |
Call Link: Click, Email Link: Click, SMS Link: Click, WhatsApp Link: Click | number or email (your own published details), text, page | Contact clicks |
404 | path | Not-found page title |
| Smart events | Named by you in the Analytics page | Smart events (rules run with no code) |
Page views, time engaged, scroll depth and Core Web Vitals are measured too, and a sample of clicks for heatmaps.
msa('ready', callback) and msa('get')
With Attribution. msa('ready', fn) calls fn(state) once the visitor's IDs are known (right away if they
already are). msa('get') returns the same state, or null before it's ready.
msa('ready', (s) => {
console.log(s.browser_id, s.touches.last_marketing && s.touches.last_marketing.channel);
});
| Field | Meaning |
|---|---|
siteId | The website's ID |
browser_id | This browser's ID (32 hex characters) |
device_id, person_id, household_id | Matched IDs, when known (a person ID after identify or a form fill) |
session_id, new_session | Attribution's session and whether this page started it |
ga_client_id, ga_session_id | GA4's IDs, when GA4 runs on the page |
touches | first_touch, last_touch, last_marketing and last_paid, each with source, medium, channel, campaign, content, term, click ID type and time |
msa('identify', emailOrUserId)
Links this browser to a known person, for example after sign-in, so their visits on other devices join up. Only a SHA-256
hash of the trimmed, lower-cased value leaves the page. An email also helps Google Ads match conversions when ad consent is given.
The visitor's person_id is then available from msa('get').
msa('identify', 'dana@example.com');
Fill forms with attribution data
With Attribution on, the tag writes the visitor's source, campaign and IDs into your forms, so every lead reaches your CRM or inbox with where it came from. No code is needed: add fields with the right names and the tag fills them as soon as the page is ready, again when the visitor's IDs arrive, and in forms your page adds later.
How a field is found
For each value below, the tag fills every input and textarea that has, with the default prefix ms_:
name="ms_source", orid="ms_source", orclass="ms_source"on the field itself, orclass="ms_source"on an element around it (form builders that let you set a CSS class on a field's wrapper, such as WPForms, Gravity Forms, Elementor or HubSpot's embedded forms).
<form action="/contact" method="post">
<input name="email" type="email">
<input type="hidden" name="ms_source">
<input type="hidden" name="ms_medium">
<input type="hidden" name="ms_campaign">
<input type="hidden" name="ms_first_touch_source">
<input type="hidden" name="ms_browser_id">
<button>Send</button>
</form>
<!-- A form builder that only lets you set a class on the field's wrapper -->
<div class="wpforms-field ms_campaign"><input type="hidden" name="wpforms[fields][7]"></div>
Use hidden fields where you can. In a visible field, a value the visitor typed is never replaced. The tag sets values so that
React, Vue and similar libraries notice (it fires input and change). Form submits are never blocked or changed.
Field names
The unprefixed touch fields describe the visit that matters for the lead: by default the visitor's last marketing touch (their last visit that wasn't direct: from a campaign, ad, search engine, social site or referral), or their last visit when every visit was direct.
| Field | Value |
|---|---|
ms_source | Source, e.g. google, facebook, newsletter (utm_source, or worked out from the click ID or referrer) |
ms_medium | Medium, e.g. cpc, organic, email, referral |
ms_channel | Channel, e.g. Paid Search, Organic Search, Email, AI Assistants |
ms_campaign, ms_content, ms_term | utm_campaign, utm_content, utm_term |
ms_utm_id | utm_id |
ms_source_platform, ms_creative_format, ms_marketing_tactic | GA4's newer UTM tags |
ms_click_id, ms_click_id_type | The ad click ID and its type (gclid, fbclid, msclkid, ttclid ...) |
ms_gclid, ms_fbclid, ms_msclkid ... | The click ID under its own name, for CRMs that expect it (only the type that was clicked is filled) |
ms_referrer | The referring site |
ms_landing_page | The page the visit started on |
ms_<param> | Ad and affiliate IDs we keep from the link (e.g. ms_irclickid), and the website's own campaign parameters (set in its tracking settings) |
ms_browser_id, ms_session_id | Our browser and session IDs (join the lead to the visitor's whole journey) |
ms_device_id, ms_person_id, ms_household_id | Matched IDs, when known |
ms_ga_client_id, ms_ga_session_id | GA4's IDs, when GA4 runs on the page |
Fields for each touch
Every touch field above is also available for a specific touch: ms_<touch>_<field>.
| Touch | Meaning | Example field |
|---|---|---|
first_touch | The visitor's first visit | ms_first_touch_source |
last_touch | The latest visit, whatever brought it | ms_last_touch_medium |
first_marketing | The first visit from marketing (not direct) | ms_first_marketing_campaign |
last_marketing | The latest visit from marketing (not direct) | ms_last_marketing_channel |
last_paid | The latest paid or tagged campaign visit | ms_last_paid_gclid |
Settings
In the website's Attribution settings in the admin page (client → Websites & Apps → the website), or through the Admin API's website settings:
| Setting | API field | Default |
|---|---|---|
| Fill forms on or off | form_fill | on |
| Which touch fills the unprefixed fields | form_touch: default, first_touch, last_touch, first_marketing, last_marketing, last_paid | last non-direct touch |
Field name prefix (e.g. lead_ makes lead_source) | form_prefix | ms_ |
| Extra CSS selector per field, for forms whose field names you can't change | form_selectors, e.g. {"source": "#hs_lead_source"} | none |
| Count form submits (never their values) | count_submits | on |
Showing values in the page
Only form fields are filled; other elements with these class names are left alone. To show a value in page text, read it yourself:
msa('ready', (s) => {
const t = s.touches.last_marketing || s.touches.last_touch;
document.querySelectorAll('.show-source').forEach((el) => { el.textContent = t ? t.source : ''; });
});
Form commands
| Call | Use |
|---|---|
msa('fill') | Fill forms again now (after your code changes a form; forms added to the page are filled anyway) |
msa('managedForms', '.wpforms-form') | These forms report success themselves (a CSS selector), so a submit only counts once the plugin confirms it |
msa('submitted', form, { plugin: 'wpforms', form_id: 12 }) | The form plugin accepted this submission |
The WordPress plugin, Shopify app and HubSpot and Salesforce installs set these up for their own forms.
dataLayer events
For Google Tag Manager, Attribution pushes {event: 'ms_attribution', ms_browser_id, ms_person_id, ms_session_id, ms_source,
ms_medium, ms_channel, ms_campaign, ms_touches, ...} on each page (and again after identify, with
ms_reason: 'identify'). The event and dataLayer names can be changed in the website's settings.
msa('debug')
msa('debug') turns Attribution's console log on for this page (msa('debug', false) off). The website's debug switch in
the admin turns it on for every visitor and also makes Analytics warn in the console about parameters it left out. Turn it off again when you're done.
Forwarding to other trackers
Every named event the Analytics tag records (your msa('event') calls, the events sent for you and smart
events) is also sent to the other trackers on your page, so you can use it there without extra code. The copy is named
ms_ + the event name, made safe for GA4: lower case, letters, digits and _, 40 characters at most.
Signup becomes ms_signup and Call Link: Click becomes ms_call_link_click.
It's on by default. To turn it off, clear "Send our events to this site's other trackers" in the website's Analytics settings,
or set forward_events: false in the website's settings.analytics with the Admin API.
| Tracker | How the event is sent | Needs ads consent |
|---|---|---|
| Google Analytics 4 (gtag.js) | gtag('event', 'ms_signup', params), with items | No |
| Google Tag Manager | dataLayer.push({event: 'ms_signup', ...params, ecommerce: {items}}), only when a GTM container is loaded; use a Custom Event trigger | No |
| Meta Pixel | fbq('trackCustom', 'ms_signup', params) | Yes |
| TikTok Pixel | ttq.track('ms_signup', params) | Yes |
| Microsoft Advertising (UET) | uetq.push('event', 'ms_signup', {event_category: 'metric_sensei', event_label, revenue_value, currency}) | Yes |
| Reddit Pixel | rdt('track', 'Custom', {customEventName: 'ms_signup', value, currency}) | Yes |
| Klaviyo | klaviyo.push(['track', 'ms_signup', params]) | Yes |
| PostHog, Mixpanel, Amplitude, Segment, Heap, Umami | Their capture / track call with the parameters | No |
| Matomo | _paq.push(['trackEvent', 'Metric Sensei', 'ms_signup', label, value]) | No |
| Plausible | plausible('ms_signup', {props: params}) | No |
| Fathom, Microsoft Clarity, Hotjar | The event name only | No |
- Not sent: page views (those tools count their own), time engaged, scroll depth, speed and heatmap clicks, and events
whose name already starts with
ms_. A tracker also doesn't get our automatic events that it records by itself: GA4 and Tag Manager (enhanced measurement), PostHog, Heap, Amplitude, Clarity and Hotjar (autocapture) get noOutbound Link: Click,File DownloadorForm: Submit, and Matomo no outbound or download clicks. Your own events, smart events, contact link clicks and404go to every tracker. An event a tracker sends back tomsawhile it's being forwarded isn't sent on again, so a GTM tag that callsmsa('event', ...)can't make a loop. - Consent: nothing is sent unless the visitor allows analytics (see consent). The ad platforms (Meta,
TikTok, Microsoft Advertising, Reddit, Klaviyo) also need ads consent:
ad_storage(andad_user_datawhen set) not denied in Google consent mode, orads: trueinmsa('consent', ...)with a banner. They never get events when the browser sends Global Privacy Control. - Parameters: the event's own (up to 25, values cut to 100 characters, names made GA4-safe). Anything that looks like an
email address or phone number is left out, and so are the
numberandemailof contact link clicks. The event itself still goes. Visitor and visit IDs are never sent. - Double counting: if you already send the same event to a tracker yourself, you'll see it twice there, once under your
name and once as
ms_…. Use one of them, or turn forwarding off. With both gtag.js and Google Tag Manager on the page, a GTM tag that sends every custom event to GA4 would also double it. - The forwarding code is a small extra file next to the tag, loaded the first time an event is forwarded, so a Content-Security-Policy that allows the tag allows it too. With the website's debug switch on, the console says where each event went.
Consent and Global Privacy Control
Each website has a consent mode (in its settings): none, Google consent mode or consent banner.
| Mode | Analytics is allowed when |
|---|---|
| None | Always |
| Google consent mode | The page's latest gtag('consent', ...) says analytics_storage: 'granted' |
| Consent banner | Your banner calls msa('consent', { analytics: true, ads: false }); with Attribution on, a TCF v2 or GPP banner is read too |
// From your cookie banner, when the visitor answers (and on each page load once they have)
msa('consent', { analytics: true, ads: false });
- Without analytics consent Analytics still counts page views and events, anonymously: no visitor ID is sent and the server counts the visitor with a daily hash it can't reverse. Analytics sets no cookies of its own.
- Global Privacy Control (
navigator.globalPrivacyControl): Analytics treats the visitor as not consenting (anonymous counting), and Attribution never shares IDs with ad platforms. msa('analytics').consentedtells you which way the visitor is counted.
msa('privacy', callback) and msa('privacy', 'forget')
For your privacy page. msa('privacy', fn) calls fn({ browser_id }) once the visitor's ID is known, so you
can show it. A visitor who asks you to see or delete their data can give you that ID, which proves the request comes from
their browser; log it under Settings › Privacy requests (or the Admin API's privacy-requests).
msa('privacy', 'forget') clears our cookies and storage on this browser, including the cookie our server keeps on your
own tracking domain. It doesn't delete what was already collected: use a privacy request for that.
msa('privacy', function (p) {
document.getElementById('my-id').textContent = p.browser_id;
});
document.getElementById('forget-me').onclick = function () { msa('privacy', 'forget'); };
Personal data
Don't send names, email addresses, phone numbers or other personal data as event parameters. As a safety net, text that looks like an
email address or a phone number (with a leading + or the (555) 555-5555 shape) is replaced with (redacted) on our
servers before it is stored. The exceptions are the events our tag makes from your own page: the contact link clicks and your
smart events, which carry the phone number, address or link text published on your site.
Plain digit strings such as order IDs are kept. Use msa('identify') for linking people; it only sends a hash.
The tag never sends URL query strings (except campaign parameters such as utm_* and click IDs), form field values, full
referrer URLs or IP addresses.
Events forwarded to other trackers never carry anything that looks like an email address or phone number, the contact clicks' own included.
Recipes
Moving from GA4's gtag
// gtag('event', 'sign_up', { method: 'Google' });
msa('event', 'sign_up', { method: 'Google' });
// gtag('set', { user_type: 'member' });
msa('set', { user_type: 'member' });
Both can run side by side; with GA4 linked, the Analytics page can show GA4's numbers next to ours or blended. GA4 also gets each
of our events as ms_sign_up (forwarding), so once you've moved an event to msa you can drop its
gtag call and use ms_sign_up in GA4.
Google Tag Manager
Put the snippet in a Custom HTML tag on Initialization - All Pages. For events, a Custom HTML tag with your trigger:
<script>msa('event', 'video_play', { title: {{Video Title}} });</script>
React, Vue and other single-page apps
// Page views are automatic with pushState routers. Events:
<button onClick={() => msa('event', 'upgrade_click', { from: 'banner' })}>Upgrade</button>
// TypeScript: declare the global once
declare global { interface Window { msa: (...args: unknown[]) => unknown } }
Server-side events
To send events from your server (a payment webhook, a CRM status change), use the Analytics events API with a server key created on the client's Analytics page:
curl -s -X POST https://analytics.api.metricsensei.com/v1/events \
-H "Authorization: Bearer msk_..." -H "Content-Type: application/json" \
-d '{"events": [{"name": "purchase", "visitor_id": "<browser_id from msa(\"get\")>",
"props": {"transaction_id": "T-10293"}, "revenue": 59.9, "currency": "USD",
"items": [{"item_id": "SKU-1", "price": 19.95, "quantity": 2}]}]}'
Up to 100 events per call, each up to 7 days old, with the same parameter rules as the browser. Pass Attribution's
browser_id as visitor_id so browser and server events join up.
Where events show up
- Analytics page: Goals & events (counts, visits, conversion rate, text values, number totals, items), Revenue, Funnels, Realtime.
- Admin API:
GET https://api.metricsensei.com/clients/{client}/analytics/sites/{site_id}/eventsreturnsrows,properties,numbersanditems. See the Admin API docs. - API/MCP Gateway: the
analytics_*tools, for AI assistants and your own code.
Reports update every 15 minutes; Realtime within a minute.
Command reference
| Command | Arguments | Returns | Service |
|---|---|---|---|
event | name, params? | true / false | Analytics |
set | params (null removes) | nothing | Analytics |
pageview | {path}? | nothing | Analytics |
analytics | callback? | info object | Analytics |
variant | experiment key | variant key or "" | Analytics add-on |
flag | flag key | true / false | Analytics add-on |
consent | {analytics, ads} | nothing | Both |
ready | callback(state) | nothing | Attribution |
get | state or null | Attribution | |
identify | email or user ID | nothing | Attribution |
fill | nothing | Attribution | |
managedForms | CSS selector | nothing | Attribution |
submitted | form, meta | nothing | Attribution |
debug | false? | nothing | Attribution |
Commands that return something return undefined while queued before the tag loads.
Troubleshooting
- Nothing arrives: check the page's host is one of the website's hosts, Analytics is on for the website, and no ad blocker
stops
cdn.metricsensei.com(first-party mode avoids that). Your browser's Network tab shows a request to/a. - An event is missing: check the name (allowed characters, no leading
$), and thatmsa('event', ...)returnedtrue. Turn on debug to see left-out parameters. - A purchase counted once although the page was reloaded: that's
transaction_iddoing its job. - Numbers show as totals, not values: send IDs and codes as text.
- A value shows as (redacted): it looked like an email address or phone number.