Tag reference
The tag's attributes, commands and privacy controls. The release build is 9,930 bytes minified (about 9.9 KB), or 4,364 bytes gzipped (about 4.4 KB).
Script attributes
data-site: the site this tag reports for. When the page's hostname is this domain or a subdomain of it, events count for the named site and the visitor cookie is shared across all its subdomains. Any other hostname falls back to plain hostname reporting.data-site-id: your site's installation id (dst_...), printed in the dashboard's snippet. It grants no access and does not prove ownership. Complete the separate DNS or homepage meta-tag check to verify the domain.data-api: where events are sent. Defaults to/api/collecton the host that servedds.js; set it when proxying through your own domain.data-hash-routing="true": for apps that route on the fragment (/#/pricing). The hash becomes part of the recorded path and a hash change counts as a pageview. Off by default, so an ordinary anchor link never counts as a new page.data-allowed-hosts="shop.acme.com,pay.acme.com": cross-domain linking. A click on a link into one of these hosts gets?_dsv=<visitor id>appended. A page carrying the attribute adopts an id it lands with, but only when the id is well-formed and the visitor arrived from a host on its own list or from the site itself, so a stranger pasting the parameter into a link cannot rename a visitor. The parameter is then removed from the address bar. Put the attribute on both sites, each naming the other, and one person is one visitor across both. Off by default.data-disable-payments="true": switches off payment detection on the success page. On by default: a Stripe Checkout Session id in the landing URL is reported once per browser session, with the visitor's cookie, so the payment can be joined to this visit. The server verifies it with your own Stripe key; the URL itself never counts as money. Details on the Stripe page.data-require-consent: waits fords("consent", "granted")without cookie or storage access, measurement requests, listeners or history patches. Calls queue only during the initial wait.deniedorrevokeddiscards that queue, stops measurement and clears accessible visitor cookies; later calls are dropped. Re-grant starts with a fresh visitor id and the current page, without replaying denied-period activity. Ungated tags start immediately but also accept withdrawal. GPC and Do Not Track always win. See consent setup and withdrawal limits.data-checkout-param="checkout_session": the query parameter that carries the Checkout Session id on your success URL, when it is not Stripe's defaultsession_id. Only an id shaped likecs_live_...orcs_test_...is ever sent.
The ds() commands
ds("goal", "signup");
ds("goal", "demo-booked", { value: 500, plan: "pro" });
ds("order", { value: 49.99, currency: "USD", id: "ord_1001" });
ds("identify", { user_id: "u_1001", email: "ada@example.com", plan: "pro" });
ds("consent", "granted");Goal names: up to 64 characters of letters, digits, spaces, _, ., / and -, starting with a letter, digit or _; anything else is rejected by the server and the goal is not stored. Orders need a value; currency is a three-letter code (anything else is recorded as USD). Every other key on a goal's object is a property: up to 10, keys of lowercase letters, digits, - and _ (lowercased by the tag), values a string, number or boolean kept as text up to 255 characters. Order ids: up to 128 characters of letters, digits, _, ., : and -, and an order with an id counts once per site. Identify takes a required user_id (up to 128 characters, no spaces), an optional email the server keeps only as a SHA-256 hash, and properties under the goal rules; it is sent with fetch rather than a beacon because the reply can carry a different visitor id when this cookie is folded into an earlier one for the same user. Details on the goals page. Consent accepts "granted", "denied" and "revoked". The latter two stop measurement even on an ungated tag. Neither denial nor withdrawal erases server history or recalls requests already sent. Calling before the tag loads? Add the queue stub from the goals page and early calls wait for it.
Click and scroll attributes
<button data-ds-goal="start-trial" data-ds-goal-value="49">
Start free trial
</button>
<section data-ds-scroll="read-pricing" data-ds-scroll-threshold="0.5">
...
</section>Any element with data-ds-goal counts that goal per click, clicks on children included. data-ds-goal-value is optional.
Any element with data-ds-scroll counts that goal once per pageview when it scrolls into view. data-ds-scroll-threshold is the share of the element that must be visible, 0 to 1, default 0.5. It uses IntersectionObserver; a browser without it gets no scroll goals and everything else unchanged. After a client-side route change the page is scanned again, at once and half a second later, so an element a framework renders after the URL changes still counts.
What the tag sends on its own
- Pageviews, on load and on every client-side route change (
pushState,replaceState,popstate, andhashchangewith hash routing on). The same path twice in a row is counted once. - Outbound clicks: a click on an
http(s)link whose host is not this site or one of its subdomains sends the destination as host and path, capped at 200 characters. They come back as the API'soutboundrows, one per destination host with clicks and visitors, and never count as a pageview. - Engaged time: how long each page was visible, sampled only while the tab is visible and capped at 30 minutes. It is reported when the tab is hidden, when the page is left (
pagehide), and on a route change. The server keeps the largest value it has seen for that pageview, so a tab that flips hidden and visible all afternoon is counted once. - Checkout returns: a Stripe Checkout Session id found in the landing URL (
?session_id=cs_...) is sent once per browser session, after the pageview it belongs to, so the server can join the payment to this visit. Nothing on the URL counts as money; Stripe is asked with your own key. Off withdata-disable-payments="true".
window.datastated
window.datastated.visitorId
window.datastated.consentThe public handle reports consent as "waiting", "granted", "denied" or "revoked"; an ungated tag starts at "n/a". visitorId exists only while measuring. Read it when you need it: identify may merge it into an earlier visitor, and withdrawal removes it. Re-grant creates a fresh id; a late identify response cannot restore the withdrawn one. Pass an active id to your server as metadata.datastated_visitor_id to match a Stripe payment to the visit.
Visitor-side switches
- Global Privacy Control / Do Not Track: the tag does nothing at all. No cookie, no events, no override.
- Automation: a browser under a driver (
navigator.webdriver), a user agent that names a headless build, a crawler or an automation tool, or the globals that Selenium, ChromeDriver, Playwright, PhantomJS and Nightmare leave onwindow(Puppeteer announces itself throughnavigator.webdriverand its user agent) gets the same silence. Nothing is sent and no cookie is set. There are no heuristics on plugins, screen size or timing, because those misfire on privacy browsers and cheap phones and would drop real people. localStorage.datastated_ignore = "true": silences the tag when it starts, for testing your own site without counting yourself. A gated tag does not read this key before consent. Delete the key and reload to count again.- URL parameters read:
utm_source,utm_medium,utm_campaign,utm_term,utm_content, the shorthandsref/via/source, andfbclid,gclid,gbraid,wbraid,gclsrc,ttclid,msclkid,twclidandli_fat_id. When several click parameters are present the first in that order wins.gclsrcmaps to Google Ads only foraw.ds; other non-empty values remain unassigned to an ad platform. A barefbclidorutm_source=metawithout a paid medium is excluded from Meta's comparison. These values classify traffic, not prove its origin. See the complete attribution rules.
Questions? Email us at hello@datastated.com.