Causiq SDK—GDPR friendly tracking

Causiq's SDK is a complement to our Media Mix Modelling (MMM). MMM is at the core a top-down method, in that it spans from marketing spend, to impressions (of the ads), all the way through to the measured goal/revenue (the KPI).

In contrast, the data that can be captured from your web properties or apps (also called a clickstream) can be used to give you granular insights about the number of visitors, which pages are popular, whether that popularity is also causatively linked to an increase in your measured KPI.

As such, the SDK and the MMM that we run using the marketing spend data are very strong complements to each other. In a way, if you have desirable products, the parameters of the function that explains how well your marketing is doing, is primarily:

  • the content and its quality
  • optimising between channels and on funnel level
  • optimising the site with uplift modelling

As such, the SDK further strengthens the analysis that we can perform on your behalf, and truly makes Causiq the Marketeer's control panel.

A GDPR-friendly Google Analytics

Causiq's SDK was designed with GDPR in mind. It has a few different key features. To start with, the SDK has a complete API for forgetting and/or resetting user identifiers. Forgetting can be used by the user on the site itself, and the resetting feature lets you easily track computers used by multiple persons.

Furthermore, you can run Causiq's SDK in a cookieless mode. Then the pseudo-anonymous identifier is only bound to the current browser session and will be forgotten when the user closes the tab.

If you don't use the identify or setUserProps functions, then by default, only a persistent, first-party, pseudo-anonymous identifier is written to the cookie jar and hrefs are logged from the browser. As such, there's no obvious way to tie this data to a specific user.

Finally and most importantly, Causiq AB is a Swedish company, so your data doesn't leave the European Union.

Setup

In the app, go to Settings > Pixel and follow the instructions to add the pixel to your site or app.

In GTM or as a pixel

Inside your <head> tag:

<script> window.causiq=window.causiq||[], causiq.init=function(m, c) { causiq.push(['init', m, c]); var s = document.createElement("script"); s.type = "text/javascript", s.async = !0, s.src = "https://api.causiq.com/v1/track/causiq.js"; var a = document.getElementsByTagName("script")[0]; a.parentNode.insertBefore(s, a); for (var n = function(e) { return function() { causiq.push([e].concat(Array.prototype.slice.call(arguments, 0))) } }, p = [ "init", "track", "error", "setPageProps", "setUserProps", "unsetUserProps", "getIdentity", "identify", "resetIdentity", "forgetUser", "navigate"], i = 0; i < p.length; i++) causiq[p[i]] = n(p[i]) }; causiq.init("CQ-H1A23LDP9ABC"); </script>

With NodeJS

If you want to use the NodeJS CommonJS module, you can install it using:

npm i @causiq/sdk

or if you're using yarn

yarn add @causiq/sdk

Example

import { Causiq } from '@causiq/sdk' const c = new Causiq() c.init('CQ-H1A23LDP9ABC')

Lifecycle

init(moniker: string, config?: Partial<Config>): void

This function call is a requirement in order to initialise Causiq.

causiq.init('CQ-H1A23LDP9ABC')

Alternatively if you want to initialise with a configuration:

causiq.init('CQ-H1A23LDP9ABC', { cookieless: false, disableTextCapture: false, })

...or if you want to test with only the console client-side to start with:

const c = new Causiq() c.init('CQ-H1A23LDP9ABC', (config, { sendWithConsole }) => ({ send: typeof window === 'undefined' ? config.send : sendWithConsole, }))

You can read more about the configuration options further below.

You can try the SDK here by opening your console and pressing the buttons by the examples, or by using the global variable causiq with the API documented below.

Config properties

Configuration properties specify how Causiq's SDK performs/acts.

disableTextCapture: boolean

The disableTextCapture option will stop Causiq from capturing contents from your pages. By default, Causiq does not capture the contents of input fields, but captures text from most other elements. You can get more granular tracking by supplying a function to the shouldCaptureText property of the configuration.

shouldCaptureText(element: Element): boolean

This function will then be called for every click; return true if the text can be captured. If you override this function, ensure you don't capture sensitive data.

causiq.init('CHANGE-ME', { shouldCaptureText: function shouldCapture(el) { return ['button'].indexOf(el.localName) !== -1 }, })
sendInterval: number
How often in milliseconds, to broadcast the list of messages to be tracked.
send(messages: InternalMessage[]) => Promise<void>

Lets you configure (for unit testing, or for novel runtimes), how to send the tracking data.

  1. sendWithConsole: only send to the console
  2. sendWithBeacon: send to api.causiq.com with beacon
  3. sendWithFetch: send to api.causiq.com with fetch
cookieName: string
Useful if you want to let Causiq read your own user id cookie (remember the cookie must only contain a 32 char long string)
cookieless: boolean
Whether to run the SDK in a fully cookieless manner; in which case it'll generate a new pseudo-anonymous identifier that is active while the script is loaded and then forgotten. You can use identify to link this session with a persistent user id.

Automatic click tracking

Causiq's SDK automatically tracks clicks by their CSS selector based on element names, class attributes and ids. This can be used after-the-fact to assign event names to selectors (virtual events). It will prefer to track a more specific selector over less specific (you can try clicking these list items):

  1. id attributes
  2. data-track attributes
  3. class attributes
  4. the name of the element, e.g. section

NOTE: data-track is meant for event names primarily; if you want better CSS Selectors, use the id attribute.

Tracing

The tracing functionality is at the core of the SDK; every time you show a page/hide a page a span is generated and sent to the tracking endpoint.

startSpan(name: string): StartedSpan
You can use this function to start child spans; it logs a minimal format of OpenTelemetry. Useful if you're interested in understanding how long your users have modal dialogs open, for example.
end(): void
Ends the current span (that was returned from startSpan).

Content analytics

setPageProps(props: Props, spanContext?: SpanContext): void
Useful for categorising pages to see which type of page attributes causes better KPI:s

Example

<button onClick="causiq.setPageProps({title:window.document.title})">Set page props</button>

Behavioural analytics

track(name: string, props?: Props, spanContext?: SpanContext): void
This is the primary method for tracking user interactions and details about e.g. products in an e-commerce shop.

Example

<button onClick="causiq.track('Add to cart', {a:1})">Add to cart</button>
navigate(prevHref: string, nextHref: string, props?: Props, spanContext?: SpanContext): void
Track navigation events that don't cause page reloads (e.g. modern JS apps)

Example

<button onClick="causiq.navigate(window.location.href, 'https://causiq.com/')">Navigate</button>
error(err: Error | ErrorEvent | string, props?: Props, spanContext?: SpanContext): void
Log Error, ErrorEvent or string messages as errors.

Example

<button onClick="(function(){throw new Error('Example error')})()">Throw error</button>

User data platform

identify(newId: string, spanContext?: SpanContext): void
Links the current pseudo-anonymous identifier created by Causiq's SDK with another identifier.

Example

<button onClick="causiq.identify('cb36c6eb59f24c54aafc7e7d9d893509')">identify</button>
getIdentity(): UserId
This function call will return the identity of the current user

Example

<button onClick="alert(causiq.getIdentity())">getIdentity</button>
resetIdentity(): UserId
This function call will reset the identity of the current user, and return the new user id. Useful if you're tracking websites that may be used on shared computers.

Example

<button onClick="causiq.resetIdentity()">resetIdentity</button>
setUserProps(props: Props, spanContext?: SpanContext): void
Associate key-value pairs/attributes/properties with the current user.

Example

<button onClick="causiq.setUserProps({nickname: 'haf'})">setUserProps({ nickname: 'haf' })</button>
unsetUserProps(...keys: string[]): void
Disassociate the keys from the user.

Example

<button onClick="causiq.unsetUserProps('nickname')">unsetUserProps('nickname')</button>

SDK API

If you are not able to use our javascript sdk for some reason. Maybe you are building a native app and can't use javascript. Then you can use our API directly like so.

The main endpoint is the track endpoint. To which you will use the HTTP verb POST to send application/json data.

Example

    POST https://api.causiq.com/v1/track

    Content-Type: application/json
    Accept: */*

Causiq Semantic Events

Causiq currently provide semantic events for ecommerce and bussiness to bussiness software as a service. And this is currently beeing developed, so more will come.

Documentation and examples of the Causiq semantic events can be found here:

Causiq Semantic Events

Payload outline

Below is a desciption of a track event payload.

ts: number
A Unix epoch Timestamp in milliseconds when the event occured.
v: string
The version string of the SDK, in the form Major.Minor.Patch.Timestamp
m: string
Compilation mode of the sdk.
Accepted values: prod
t: string
Compilation target.
Accepted values: web | node
payload: array
An array with all the messages to send
m: string
App moniker that uniquely identifies the app in Causiq
t: number
The timestamp epoch milliseconds of this event
n: string
The name of the event that is sent
Accepted values:
Each of these carry different data that should be sent in ps

Common page tracking examples:

*** for more event types please visit our event documentation here

clicked
{ cssSelector: string, eventName?: string, text?: string }
pageVisible
;[VisibilitySource, PageData]

VisibilitySource:

load | visibilitychange | pagehide | unload | navigate,

PageData:

{ /** * Identifies which site sent the traffic, and is a required parameter. */ utmSource?: string | null /** * Identifies what type of link was used, such as cost per click or email. */ utmMedium?: string | null /** * Identifies a specific product promotion or strategic campaign. */ utmCampaign?: string | null /** * Identifies search terms. */ utmTerm?: string | null /** * Identifies what specifically was clicked to bring the user to the site, such as a banner ad or a text link. It is often used for A/B testing and content-targeted ads. */ utmContent?: string | null /** * Google Ads click Id */ gclid?: string | null /** * navigator.location.href: the full URL of the page/screen. * Can also be a URI. */ readonly href: string /** * the pathname part of the URL */ readonly pathname: string /** * The title of the page/screen. */ readonly title: string /** * max(document.documentElement.clientWidth, window.innerWidth) * https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth * https://developer.mozilla.org/en-US/docs/Web/CSS/Viewport_concepts */ readonly clientWidth: number /** * max(document.documentElement.clientHeight, window.innerHeight) * https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth * https://developer.mozilla.org/en-US/docs/Web/CSS/Viewport_concepts */ readonly clientHeight: number /** * navigator.userAgent */ readonly userAgent: string /** * max(window.screen.width, window.screen.availWidth) */ readonly deviceWidth: number /** * max(window.screen.width, window.screen.availWidth) */ readonly deviceHeight: number /** * document.referrer (may be the empty string). */ readonly referrer: string | null /** * Olsen timezone */ readonly timezone: string | null /** * new Date().getTimezoneOffset() which gives the offset of UTC * from the perspective of the User's browser. */ readonly datetimeOffset: number /** * Browser or device vendor */ readonly vendor: string | null /** * Which languages are enabled in the User's browser? */ readonly languages: readonly string[] }
pageHidden
;load | visibilitychange | pagehide | unload | navigate
spanEnd
{ /** * A reference to the SpanContext this Span belongs to. */ readonly context: SpanContext /** * The operation name. E.g. if the UI spawns a modal, the name of the modal could be the * operation name. */ readonly name: string /** * When did this Span start? ms since epoch */ readonly start: Timestamp /** * Which type of Span is this? */ readonly kind: SpanKind /** * Span attributes used to provide the context the span was created and used in for human consumption. */ readonly attrs: Props /** * This span does not have an error. */ ok: boolean /** * When (if ever) did this Span end? Server-side, this is a required value. */ end?: Timestamp }
ps: array | tuple
An array/tuple, typed specifically for the type of message denoted by discriminator prop n.
s: SpanContext
A SpanContext is a simple DTO that tracks the overarching Trace Id as well as the current Span Id. More docs at opentracing.io
spanId: string
The 16 character Span Id. The Id of the Span. It is globally unique with practically sufficient probability by being made as 8 randomly generated bytes, encoded as a 16 lowercase hex characters corresponding to 64 bits.
parentSpanId?: string
A optional 16 character Parent span Id.
traceId: string
The 32 character Trace Id The Id of the trace that this span belongs to. It is worldwide unique with practically sufficient probability by being made as 16 randomly generated bytes, encoded as a 32 lowercase hex characters corresponding to 128 bits.
traceFlags: number
It is represented as 1 byte (bitmap). Bit to represent whether trace is sampled or not. When set, the least significant bit documents that the caller may have recorded trace data. A caller who does not record trace data out-of-band leaves this flag unset.
Available values:
0: None
1: Sampled
u: string
By default, a 32 characters (128 bits) long hexadecimal string denoting a User Id.

Example of SDK-evens payloads

Below are to two most common events, Order Completed and Account created, listed with their corresponding payloads.

Order Completed (minimal) payload:
{ "ts": 1666897447941, "v": "2.0.0.1666786511242", "m": "prod", "t": "node", "payload": [ { "m": "CQ-H1A23LDP9ABC", "t": 1666897447938, "n": "track", "ps": ["Order Completed", { "orderId": "order-123", "total": 27, "currency": "EUR" }], "s": { "spanId": "eeb77dc8554d4ca3", "traceId": "132f9f36f634457cafc7b81c44d334c0", "traceFlags": 1 }, "u": "77c3b9fbbf35406fbd416556fc992d68" } ] }
Order Completed (complete) payload:
{ "ts": 1666897447941, "v": "2.0.0.1666786511242", "m": "prod", "t": "node", "payload": [ { "m": "CQ-H1A23LDP9ABC", "t": 1666897447938, "n": "track", "ps": ["Order Completed", { "checkoutId": "456", "orderId": "order-123", "products": [{ "productId": "987", "sku": "789", "name": "Product 1", "price": 10, "salesPrice": 8, "quantity": 3, "category": "Category 1", "url": "https://example.com/product-1", "imageUrl": "https://example.com/product-1.jpg" }], "subtotal": 24, "tax": 6, "discount": 3, "total": 27, "currency": "EUR", "coupon": "ERIKA2021Q3" }], "s": { "spanId": "eeb77dc8554d4ca3", "traceId": "132f9f36f634457cafc7b81c44d334c0", "traceFlags": 1 }, "u": "77c3b9fbbf35406fbd416556fc992d68" } ] }
Account Created payload:
{ "ts": 1666897447941, "v": "2.0.0.1666786511242", "m": "prod", "t": "node", "payload": [ { "m": "CQ-H1A23LDP9ABC", "t": 1666897447938, "n": "track", "ps": ["Account Created", { "accountId": "account-123", "accountName": "Acme Inc" }], "s": { "spanId": "eeb77dc8554d4ca3", "traceId": "132f9f36f634457cafc7b81c44d334c0", "traceFlags": 1 }, "u": "77c3b9fbbf35406fbd416556fc992d68" } ] }

which will yield a successful 200 Ok response like so:

{ "error": false, "message": "ok" }

or a 400 Bad Request response:

{ "error": true, "message": "Failed schema validation", "payload": [ { "instancePath": "", "schemaPath": "#/required", "keyword": "required", "params": { "missingProperty": "m" }, "message": "must have required property 'm'" } ], "input": { "ts": 1666897447941, "payload": [ { "m": "CQ-H1A23LDP9ABC", "t": 1666897447938, "n": "pageHidden", "ps": [ "visibilitychange" ], "s": { "spanId": "eeb77dc8554d4ca3", "traceId": "132f9f36f634457cafc7b81c44d334c0", "traceFlags": 1 }, "u": "77c3b9fbbf35406fbd416556fc992d68" } ] } }

What's next?

You’re here for answers, we’re here to help.