File DA-004 View as Markdown
Script tag
This page is the reference for the snippet: where it goes, every attribute and init option, the CSP lines and how to test on localhost. If you only want it installed, your stack page is shorter.
<script>window.doubleagent=window.doubleagent||{q:[],push(){this.q.push(arguments)}};</script>
<script async src="https://cdn.doubleagent.so/v1/doubleagent.js" data-profile="auto"></script>
Put both lines in the <head> of every page, before your other scripts, and keep them in this order. The first line is a small queue: calls you make before the SDK loads wait there and run once it is ready. Load the SDK once per page.
Attributes
| Attribute | Default | What it does |
|---|---|---|
data-key | none (keyless) | Your public key, pk_live_… or pk_test_…. Safe to put in HTML. See Capabilities for what it adds. |
data-profile | auto | What kind of site this is: ecommerce, saas, leadgen and others. It changes how strict the recommendation is per action. See Site profiles. |
data-live | on for pk_test_, off for pk_live_ | Live presence for the portal's Live page: one small ping per open tab every few seconds. Needs a key. |
data-cloud | on | off sends nothing to Double Agent. Verdicts, tool tagging and your own plugins still run in the browser. |
data-site-search | on | off stops sending site-search terms. |
data-ecommerce | on | off stops sending basic ecommerce data (products viewed, cart adds, checkouts, orders). The older data-product-metadata="off" does the same. |
data-endpoint | https://api.doubleagent.so | Only for a self-hosted API. |
To turn something off, "off", "false" and "0" all work.
Options
Everything else goes in a queued init. Put it right after the queue line. Its options are merged over the attributes, and init wins where both set the same thing.
<script>window.doubleagent=window.doubleagent||{q:[],push(){this.q.push(arguments)}};
doubleagent.push('init', { consent: 'auto', block: { adConversions: 'bot+agent' } });</script>
<script async src="https://cdn.doubleagent.so/v1/doubleagent.js" data-profile="auto"></script>
This is the full list. The Browser API links here for it.
| Option | Default | What it does |
|---|---|---|
key, profile, endpoint, live, cloud, siteSearch, ecommerce | from the tag | Same as the attributes above. cloud: false, siteSearch: false, ecommerce: false. |
consent | 'auto' | 'auto' reads your consent banner, Google Consent Mode or Shopify's privacy API. 'granted' or 'denied' sets it yourself. |
mode | 'analytics' | 'security' lets security checks run without analytics consent. Analytics and ad tools still wait for consent. |
integrations | 'auto' | Which tools get the labels. 'auto' finds them on the page. { ga4: true, meta: false } picks them by hand; a tool can take its own settings, e.g. { ga4: { trafficType: true } }. |
block.adConversions | 'bot' | Which visitors' ad conversions are held back: 'bot', 'bot+agent' (unverified agents too) or 'none'. See Meta, TikTok and Google Ads. |
block.forms | false | true stops Mailchimp signup forms from submitting when the visitor is very likely a bot. |
content | on | Page types and content roles on each page. false turns them off. See below. |
journey | on | Sessions across tabs and the visitor id. { sessionTimeout: 60 } sets the idle timeout in minutes (1 to 240, default 30). false keeps one session per tab and no visitor id. |
plugins | none | Your own plugins: [plugin] or [[plugin, config]]. Same as use(). See Plugins. |
debug | false | Logs what the SDK does with console.debug. |
Page types and content roles are detected on their own. When the guess is wrong for your site, correct it with content. Rules, selectors and page text never leave the page; only the type and role names are sent.
doubleagent.push('init', {
content: {
pages: [{ match: '^/stays/[^/]+$', type: 'detail' }], // up to 50 rules, tested on the path; first match wins
roles: { '.pdp-delivery-note': 'shipping' }, // CSS selector to role
},
});
You can also mark the HTML itself: data-da-page="pricing" on <html> or <body>, and data-da-role="add_to_cart" on an element. These attributes beat both the rules and the automatic guess.
With npm instead of the CDN:
import { doubleagent } from '@doubleagent-so/js';
doubleagent.init({}); // keyless
doubleagent.init({ key: 'pk_live_xxx' }); // with a key; after a keyless start this upgrades in place
Site search
Each session in the portal shows what the visitor searched for on your site (Searched on site) and the title of the page they landed on. See The portal for where this shows.
The SDK reads the term from the page URL only, from these query parameters: q, s, search, query, keyword, keywords, k, term, searchTerm and search_query. That covers Shopify's /search?q= and WordPress's ?s=. Before the term leaves the page, email addresses and runs of 6 or more digits (phone, card and order numbers) become [redacted], and the server does the same again. Terms are sent only with analytics consent, are capped at 100 characters, and are kept with the session's pages for 90 days. Redaction catches patterns, not names or addresses, so keep personal data out of your search URLs.
To turn it off, use data-site-search="off" or init({ siteSearch: false }). The landing page title is still sent.
Content Security Policy
If your site sends a CSP header, add these:
script-src https://cdn.doubleagent.so
connect-src https://api.doubleagent.so
worker-src blob:
With data-cloud="off" you only need script-src.
worker-src blob: lets the SDK do its proof of work in a background worker. Without it the SDK still works: it does the same work on the main thread, and the browser logs a CSP warning. The queue line is an inline script, so a strict CSP needs a nonce or a hash for it. You can also leave it out if nothing calls window.doubleagent before the SDK loads.
Testing on localhost
Use your site's test key (pk_test_…) on your machine. It works on localhost, *.localhost, 127.0.0.1 and [::1], on any port, with no domain to add. To see those sessions, switch the portal's Data control from Live to Test. They show even before your real domain is verified. Use the live key (pk_live_…) in production; it only works on verified domains.
Next: read the Verdict the SDK returns, or check what a key adds in Capabilities.