Tracking API
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?)
| Argument | Type | Description |
|---|---|---|
eventName | string | The event name. Must match ^[a-z0-9_]{1,64}$ (lowercase, numbers, underscores). Invalid names are dropped. |
params | object (optional) | Key/value metadata for the event. |
linkingId | string (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?)
| Argument | Type | Description |
|---|---|---|
userId | string | A stable, pseudonymous internal ID (1–128 characters), such as a database ID. Never an email address or name. |
props | object (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 consent | What happens |
|---|---|
| Granted | The user ID is stored in the browser and sent with every following event. |
| Not answered yet | The call is kept in memory and applied if the visitor grants consent on the same page. Nothing is stored or sent before that. |
| Denied | The 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:
| Flow | Linking ID | Events |
|---|---|---|
| Checkout completes in another browser | Cart or checkout ID | begin_checkout → purchase |
| B2B quote | Quote ID | form_submit → quote_accepted |
| Demo request | Form submission ID | form_submit → demo_booked |
| Support | Ticket ID | chat_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 arefundto itspurchase(transaction_iddoes 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 })
| Argument | Type | Description |
|---|---|---|
analytics | boolean (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. |
marketing | boolean (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.
What happens before and after consent
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 granted | Analytics not answered / denied | |
|---|---|---|
| Events recorded | All | All, anonymously |
| Stored in the browser | Per tracking mode | Nothing |
| Sessions | Per tracking mode | Server-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 granted | Marketing not answered | Marketing denied | |
|---|---|---|---|
| Ad click IDs | Kept for the session (sessionStorage) | Read from the landing URL only, nothing stored in the browser | Ignored |
| Ad platform (CAPI) forwarding | Immediate | Held on our servers for up to 30 minutes, then forwarded if marketing is granted in that time, otherwise deleted | Never |
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 })
Sites that don't need consent
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.