Skip to content

Install

  1. Create a property at app.snipform.io for the domain you are tracking and copy its property key.

  2. Add the script to every page, before </body> or in <head> with defer:

    <script src="https://cdn.snipform.io/api/analytics/signals.js?site=YOUR_PROPERTY_KEY" defer></script>
  3. Open the page. The property’s live view in the dashboard shows the session within a few seconds.

The script reads its configuration from its own src, so the query string is the configuration. There is no init call and nothing to configure in JavaScript.

ParameterRequiredEffect
site (alias p)yesThe property key. Without it the script loads and does nothing.
debug=truenoLogs every lifecycle step, ping and write to the console, prefixed [Signals].
shopify=truenoTurns on the Shopify cart bridge. See Shopify.
<script src="https://cdn.snipform.io/api/analytics/signals.js?site=YOUR_PROPERTY_KEY&debug=true" defer></script>
URLBehaviour
https://cdn.snipform.io/api/analytics/signals.jsRolling. Always the current release (1.4.1 today), cached for one hour.
https://cdn.snipform.io/api/analytics/signals.v1.4.1.jsPinned. Immutable, cached forever.

Use the rolling URL unless you have a change-control reason not to. Releases never change the public API described in these docs without a major version.

If you send a CSP, allow the two hosts the tracker talks to:

Content-Security-Policy: script-src 'self' https://cdn.snipform.io; connect-src 'self' https://api.snipform.io

The session id is resolved by the server on the first request, so it is not available synchronously at script load. Two ways to wait for it:

const sessionId = await signals.ready;

signals.sessionId is the same value as a plain getter, null until ready. (signals.userId is a legacy alias for the same value; it has always been the session id, never a user identity.)

Calling signals() before the tracker is ready logs a warning and resolves { success: false, error: 'Not initialized' }. It does not queue. If an event can fire before the page has settled, await signals.ready first.

GET https://api.snipform.io/v2/signal/load with these headers. Titles, URLs and referrers are URI-encoded because header values must be ISO-8859-1.

HeaderValue
X-SNIP-IDproperty key
X-SNIP-PAGElocation.href
X-SNIP-TITLEdocument.title
X-SNIP-SRCdocument.referrer
X-SNIP-STATUSHTTP status of the page, 200
X-SNIP-DIMviewport as WIDTHxHEIGHT
X-SNIP-LIBscript version

The response is { success, session_id, view_id }. If success is false (unknown key, domain not registered, account paused) the tracker stops for that page and logs the message when debug=true.

The script guards against double inclusion with window.__snipformSignals. A second tag on the same page logs Script already loaded, skipping duplicate and does nothing, so a tag in a shared layout plus one in a component is harmless.

  • Dashboard live view shows the session and the page.
  • signals.sessionId in the console returns a string.
  • With debug=true, the console shows Initialized v1.4.1, then View loaded: <view id>, Session: <session id>.
  • The network tab shows the load request with a 200, then ping requests as you scroll and switch tabs.

If the load returns 200 but success: false, the message tells you why: the key was not found, the page’s domain does not match the property’s registered base domain (example.local is not a valid domain), or the property’s signal is not active. Subdomains of the registered domain are accepted; unrelated hosts, including a local dev server, are not - verify on a host under the property’s domain.