Events & Goals

Tracking API

The client-side otus() API for sending custom events, identifying users, linking events and passing consent.

The tracking snippet defines a global otus() function for sending your own events and attaching context. You can call it anywhere on the page at any time: calls made before the tracker has loaded are queued and sent once it has (queued consent is applied before the first page view).

track

Send a custom or standard event.

otus('track', eventName, params?, linkingId?)
ArgumentTypeDescription
eventNamestringThe event name. Must match ^[a-z0-9_]{1,64}$ (lowercase, numbers, underscores). Invalid names are dropped.
paramsobject (optional)Key/value metadata for the event.
linkingIdstring (optional)A key that connects this event to related events in other sessions or on other devices. See Linking events.
// Simple custom event
otus('track', 'newsletter_signup');

// With parameters
otus('track', 'signup_completed', { plan: 'growth', method: 'email' });

// A standard e-commerce event
otus('track', 'add_to_cart', { item_id: 'SKU-1', quantity: 1, value: 49.9, currency: 'EUR' });

identify

Tells Otus which logged-in user the visitor is, so the same person is recognised as one user across devices and sessions. Call it after login, and on every page load while the user is logged in (calling it again with the same ID does nothing).

otus('identify', userId, props?)
ArgumentTypeDescription
userIdstringA stable, pseudonymous internal ID (1–128 characters), such as a database ID. Never an email address or name.
propsobject (optional)User properties to set at the same time.
otus('identify', 'u_8f3a2c', { plan: 'growth', role: 'admin' });

User identification is only available in full tracking mode, and only takes effect once the visitor has granted analytics consent:

Analytics consentWhat happens
GrantedThe user ID is stored in the browser and sent with every following event.
Not answered yetThe call is kept in memory and applied if the visitor grants consent on the same page. Nothing is stored or sent before that.
DeniedThe call is ignored.

In cookieless mode the call is ignored, with a warning in the browser console.

Otus never stores the ID you send: it's replaced by a pseudonym on our servers before it's saved, and that pseudonym is what reports and ad platforms see. Values that look like an email address are rejected.

Once a browser has been identified, its earlier anonymous visits (with analytics consent) are counted towards the same user. Unique user counts for past periods can therefore drop slightly after someone logs in for the first time.

If identify is called with a different ID than the one stored in the browser, Otus first runs reset, so two people sharing a device are never merged.

setUserProperties

Attach persistent properties that are then sent with every subsequent event from that visitor. Useful for plan tier, account type, or experiment group.

otus('setUserProperties', {
  plan: 'growth',
  role: 'admin'
});

User properties follow the same rules as identify: full tracking mode only, stored and sent once the visitor has granted analytics consent, and kept in memory until then. They're remembered in the browser, so you don't need to set them again on every page. You don't need to call identify to use them.

Keep user properties and event parameters to non-identifying attributes: no names, email addresses or other personal data. Properties whose value looks like an email address are dropped. To link a person, use identify.

To compare logged-in visitors with everyone else, set a user property when they log in, for example otus('identify', userId, { account_type: 'customer' }), and filter reports on that property.

reset

Forgets the identified user and their properties, and starts a new visitor and session. Call it on logout so the next person on the same device isn't counted as the previous user.

otus('reset');

Linking events

The linkingId argument of track connects events that belong together but don't happen in the same session, such as a checkout that completes in another browser after a bank app redirect, or a quote that's accepted days later on another device. Use any ID your site already has for the thing the events share:

FlowLinking IDEvents
Checkout completes in another browserCart or checkout IDbegin_checkout → purchase
B2B quoteQuote IDform_submit → quote_accepted
Demo requestForm submission IDform_submit → demo_booked
SupportTicket IDchat_started → ticket_resolved
// Laptop: checkout page
otus('track', 'begin_checkout', { value: 89, currency: 'EUR' }, 'cart_7f2a');

// Another browser: confirmation page after the bank app
otus('track', 'purchase', { transaction_id: 'T-1042', value: 89, currency: 'EUR' }, 'cart_7f2a');

When an event shares a linking ID with an earlier event in a different session (within 30 days), its conversion and revenue are credited to the earlier session's source, campaign and landing page. The purchase above is attributed to the campaign that started the checkout instead of showing up as direct traffic or a referral from the bank.

  • A linking ID is only kept when the visitor has granted analytics consent, in either tracking mode. If you want to link events, ask for consent.
  • It's never stored in the browser and never sent to ad platforms.
  • It must not identify a person: no user ID, email address or customer number. Values that look like an email address are dropped. People are linked with identify.
  • You don't need it for events in the same session, for a logged-in user (use identify), or to connect a refund to its purchase (transaction_id does that).

setConsent

Tells Otus which categories of processing a visitor has consented to. Call it from your cookie banner / consent management platform (CMP) whenever the visitor makes or changes a choice, the same way you'd call Google Consent Mode's gtag('consent', 'update', …).

otus('setConsent', { analytics, marketing })
ArgumentTypeDescription
analyticsboolean (optional)Consent to store identifiers in the browser for analytics (the session ID, visitor ID, user ID and user properties used by full tracking) and to link events across sessions.
marketingboolean (optional)Consent to keep ad click IDs (gclid, fbclid, …) and forward events to your connected ad platforms for server-side conversion tracking (CAPI).

Only the categories you pass are changed. The choice is remembered in localStorage (otus_consent), so you only need to call setConsent when the visitor makes or changes a choice, not on every page load.

Each category is granted, denied or not answered yet. Otus never assumes consent: until the visitor answers, a category is treated as not granted. Visits are still counted either way, so you don't lose traffic data while a banner is open or when a visitor declines.

Analytics grantedAnalytics not answered / denied
Events recordedAllAll, anonymously
Stored in the browserPer tracking modeNothing
SessionsPer tracking modeServer-side, cookieless-style
Returning visitors (full mode)✅—
User ID and user properties (full mode)✅— (not answered: kept in memory until granted on the same page)
Linking IDs✅—
Marketing grantedMarketing not answeredMarketing denied
Ad click IDsKept for the session (sessionStorage)Read from the landing URL only, nothing stored in the browserIgnored
Ad platform (CAPI) forwardingImmediateHeld on our servers for up to 30 minutes, then forwarded if marketing is granted in that time, otherwise deletedNever

So when a visitor lands from an ad and accepts the banner a few pages later, the conversion is still attributed to the original click, and nothing was sent to the ad platform before they agreed.

Withdrawing consent (true → false) removes what Otus stored for that category: the session ID, visitor ID, user ID and user properties for analytics, and stored click IDs for marketing.

// Visitor accepted everything
otus('setConsent', { analytics: true, marketing: true });

// Visitor accepted analytics only: full reports, but nothing is ever
// forwarded to Google/Meta/TikTok/OpenAI conversion APIs
otus('setConsent', { analytics: true, marketing: false });

// Update one category; the other keeps its current value
otus('setConsent', { marketing: false });

Waiting for your CMP

If your CMP loads at the same time as Otus, the first page view may be sent before the CMP has reported a stored choice. Add consentWait (milliseconds, max 10000) to otus.init() to hold the first page view and any events until setConsent is called or the time runs out, similar to Google's wait_for_update. It only applies when the visitor has no stored choice yet.

otus.init({ siteId: 123, endpoint: "https://ingest.otusanalytics.com/e", consentWait: 500 })

If your site is not subject to consent requirements (for example, no EU/UK visitors), add consentDefault: "granted". Categories the visitor hasn't answered are then treated as granted. An explicit setConsent call still takes precedence. Make sure this matches your legal obligations.

otus.init({ siteId: 123, endpoint: "https://ingest.otusanalytics.com/e", consentDefault: "granted" })

The overview report shows the share of sessions that granted, denied or never answered each category.

Opting a visitor out

To stop the tracker recording in a given browser, set a flag in localStorage:

localStorage.setItem('otus_ignore', 'true'); // stop tracking
localStorage.removeItem('otus_ignore');      // resume tracking

How events are sent

Events are delivered with fetch and keepalive: true, so they survive page unloads and navigations. Requests never include cookies. Delivery is fire-and-forget: failures never throw or interrupt your page.

Firing events from Google Tag Manager

You can call otus('track', ...) from a GTM Custom HTML tag tied to a trigger. See the GTM guide.