# Dbrij Glass Documentation

Everything you need to run Dbrij Glass: the web tag and its options, privacy and masking, custom events and identify, consent, the Swift, Kotlin, Flutter and React Native trackers, the collector API, site settings and limits.

Canonical page: https://dbrij.com/products/glass/docs

## Introduction

**Dbrij Glass** shows you what people actually do on your website and in your apps: sessions, pages, clicks, rage clicks and dead clicks, scroll depth, heatmaps, funnels, retention and session replays. A website gets one script tag. A native app gets a single file tracker for Swift, Kotlin or Flutter, or the `@dbrij/glass-native` package for React Native. Everything lands in the same Glass dashboard.

Every site you add under Glass, Sites gets a **site key** that starts with `gk_`. The key only lets a tracker send events to that site. It cannot read anything back, so it is safe in your page source and in an app bundle. If you ever need a new one, rotate it from the site's settings: the old key stops being accepted within a minute.

Web tracker`https://glass.dbrij.com/glass.js`

Collector`https://api.dbrij.com/api/v1/public/glass`

### What the web tracker records on its own

- Page views, including route changes in a single page app (the browser history API and back and forward).
- Clicks, with the position on the page and the page size (so a heatmap holds at every screen width), a selector for the element and a short readable label.
- **Rage clicks**: three or more clicks inside one second, within 30 pixels of each other or on the same element.
- **Dead clicks**: a click on something that is not interactive, after which nothing on the page changed for 500 milliseconds and the address stayed the same.
- Scroll depth: the furthest point reached on each page, as a percentage.
- The referrer, the `utm_source`, `utm_medium` and `utm_campaign` of the landing address, and the screen size.
- A session replay, for the share of sessions the site samples (see Site settings).

No cookies and no fingerprinting. A visitor is a random id the tracker makes and keeps in the browser's local storage. A session ends after 30 minutes without activity, or after 4 hours in total, whichever comes first.

## Install on a website

Add the site under Glass, Sites, copy its tag, and paste it before `</body>` on every page (or in `<head>`; it is deferred either way). Events arrive within seconds of the next visit.

The tag

```
<script src="https://glass.dbrij.com/glass.js" data-site="gk_your_site_key" defer></script>
```

The tracker sends events in batches: every 5 seconds, or as soon as 20 are waiting, and once more when the page is hidden or closed. Replays are recorded by a second file, `glass-record.js`, which the tag fetches from its own origin only for a session the replay sample picked, so the tag itself stays small on every page.

The tag never breaks your page. Every part of it is wrapped, and a failure is silent. Without a `data-site` attribute it writes one warning to the console and does nothing else.

### Content Security Policy

If your site sends a Content Security Policy, allow the Glass host in `script-src` (it serves both files) and the Dbrij API in `connect-src`.

CSP

```
script-src 'self' https://glass.dbrij.com;
connect-src 'self' https://api.dbrij.com;
```

### Sites on Dbrij Hosting

A web service you host on Dbrij needs no paste at all. The Sites page lists your hosted web services; start watching one there and the tag is added at the hosting edge.

### Single page apps

Nothing extra to wire. The tag follows route changes made through the history API and records each new page. If your app has a signed in area you would rather not record, see `glass.appMode()` under Custom events and identify, and `data-replay-pause` under Tag options.

## Tag options

Options are attributes on the script tag. Each one applies to that install only; the site's own settings (Site settings) apply everywhere.

ATTRIBUTES

|  |  |  |
| --- | --- | --- |
| data-site* | string | The site key, gk_… from Glass, Sites. |
| data-endpoint | string | Where the tracker sends. A full https address, or a path read against the script's own origin. Leave it out to use the Dbrij API. |
| data-replay | "off" \| "masked" | "off" never records the screen on this install; events still arrive. "masked" records with every piece of text and every input masked, and images, pictures, video, audio, canvas, frames, objects and embeds left out: layout, navigation and clicks stay visible. |
| data-replay-pause | string | Comma separated path prefixes where replay must not run, such as "/account,/billing". Each prefix covers the path and everything under it: /mail covers /mail/inbox, not /mailing. Entries must start with /. |
| data-scrub | "ids" | Treats paths that differ only by an identifier as the same page, so moving between them records no new page view. An identifier is a UUID, a segment of 16 or more letters, digits, underscores or hyphens, or a number of 3 or more digits. Query strings are ignored for this too. |
| data-labels | "hooks" | Click labels come only from data-glass-id, data-testid or aria-label, never from the text on the page. Use it where rows show people's names. |

A signed in app

```
<script src="https://glass.dbrij.com/glass.js"
  data-site="gk_your_site_key"
  data-replay="masked"
  data-replay-pause="/settings,/billing"
  data-scrub="ids"
  data-labels="hooks"
  defer></script>
```

On a single page app, moving onto a paused path ends the current recording at once, and moving off it starts a fresh one with its own snapshot.

## Privacy and masking

**Inputs are always masked** in replays, at every masking level and on every plan. What a person types into a field never leaves the browser.

MASKING LEVELS (SITE SETTINGS)

|  |  |  |
| --- | --- | --- |
| strict | level | Every piece of text is masked in replays, and clicks carry no label. |
| balanced | level (default) | Text is masked inside any element with the glass-mask class or the data-glass-mask attribute. |
| relaxed | level | Only inputs are masked. |

MARKUP YOU CONTROL

|  |  |  |
| --- | --- | --- |
| class="glass-mask" / data-glass-mask | attribute | Masks the text inside the element (at the balanced level; strict masks all text anyway). |
| class="glass-block" / data-glass-block | attribute | Leaves the element and everything in it out of the recording entirely, at every level. |
| data-glass-id | attribute | A stable name for an element. Used as the click label and in the selector, so a heatmap and a funnel survive a redesign. |

Example

```
<div class="glass-mask">Balance: ₦240,000</div>
<section data-glass-block>…medical history…</section>
<button data-glass-id="checkout-pay">Pay now</button>
```

### Click labels

A click's label is read from the clicked element or up to three of its parents, in this order: `data-glass-id`, `data-testid`, `aria-label`, the element's own text when it is 60 characters or fewer, then `alt`, `title`, `placeholder` and `name`. A label never reads a field's value, and it is cut to 60 characters. With `data-labels="hooks"` only the first three are used; at the strict level no label is sent.

### What the recorder never captures

- What anyone types into an input, select or text area.
- Frames from another origin.
- Anything marked `glass-block`.

### How long a replay runs

A session records at most 60 minutes of replay in total. The recorder pauses after 3 minutes with no mouse, key, touch, wheel or scroll input, and starts a fresh segment on the next input. A tab left open records nothing.

### What the browser keeps

|  |  |  |
| --- | --- | --- |
| glass:vid | localStorage | The random visitor id. |
| glass:s:<key prefix> | localStorage | The current session: its id, when it started and when it was last active. |
| glass:cfg:<key prefix> | sessionStorage | The site's tracker settings, kept for 5 minutes. |

## Custom events and identify

Once the tag has loaded, `window.glass` carries a small API.

WINDOW.GLASS

|  |  |  |
| --- | --- | --- |
| track(name, props?) | function | Records a moment your code names, such as "signup". The name is cut to 120 characters. props is a plain object; when its JSON is over 4 KB the props are dropped and the event kept. Custom events show under Events and can be funnel steps. |
| identify(id, traits?) | function | Ties this visitor to your own user id (up to 200 characters). traits merge into that person's profile on the site. Ignored when the browser sends Global Privacy Control. |
| stop() | function | Sends what is already queued, then stops all tracking and recording for this page load. |
| appMode(mode?) | function | For the moment somebody signs in to a single page app. Turns on data-scrub="ids" and data-labels="hooks". "events" (the default) ends replay; "masked" ends the current recording and starts a masked one, as data-replay="masked", on the next input. |
| session() | function | The current session as { site, id, replay }, or null once stopped. |
| version | string | The tracker version. |

Events and identity

```
glass.track('signup', { plan: 'pro', source: 'pricing' });

// after your own sign in succeeds
glass.identify('user_1234', { plan: 'pro', company: 'Acme' });

// on a single page app, once the pages become personal
glass.appMode('masked');
```

### Calls made before the tag loads

Calls can be queued before the script arrives. Put them in `window.glass.q` as arrays; the tag replays `track` and `identify` calls from the queue once it starts, then replaces `window.glass` with the API above.

Queue, then call

```
<script>
  function glassCall(method, a, b) {
    var g = window.glass;
    if (g && typeof g[method] === 'function') return g[method](a, b);
    (window.glass = g || { q: [] }).q.push([method, a, b]);
  }
  glassCall('track', 'pricing_viewed', { plan: 'pro' });
</script>
<script src="https://glass.dbrij.com/glass.js" data-site="gk_your_site_key" defer></script>
```

## Consent and opting out

Glass sets no cookies, reads no fingerprint and records no typed input. Nothing is collected before the script runs, so if your site asks for consent before analytics, add the tag only after the visitor agrees.

Load after consent

```
function startGlass() {
  var s = document.createElement('script');
  s.src = 'https://glass.dbrij.com/glass.js';
  s.setAttribute('data-site', 'gk_your_site_key');
  s.defer = true;
  document.head.appendChild(s);
}
```

WAYS TO STOP OR NARROW TRACKING

|  |  |  |
| --- | --- | --- |
| glass.stop() | web | Withdraws for the rest of the page load. Leave the tag out on later pages to keep it withdrawn. |
| Global Privacy Control | web | When the browser sends it, the session is never recorded and identify() is ignored. Page views, clicks and custom events still arrive, under the random visitor id. |
| data-replay="off" | web | Events only, never a recording, on that install. |
| disabled / setDisabled(true) | native | Start a native tracker disabled, or switch it off at run time. Nothing is stored or sent while disabled; the queue is cleared and identify() calls are dropped. |
| Site status: paused | site | Every tracker on the site is told to stop, and anything that still arrives is dropped. |

## Native apps

Native apps send sessions, screens, taps and custom events to the same site key as your website; the dashboard shows the device as iOS or Android and the browser as `App <version>`. Session replay is web only: native sessions appear without a recording.

Pure native apps use one file per platform with no dependencies: [glass-tracker.swift](https://dbrij.com/glass-tracker.swift), [GlassTracker.kt](https://dbrij.com/GlassTracker.kt) or [glass_tracker.dart](https://dbrij.com/glass_tracker.dart). React Native uses `@dbrij/glass-native` from npm. The API is the same everywhere.

THE API ON EVERY PLATFORM

|  |  |  |
| --- | --- | --- |
| screen(name) | method | A screen became visible. The name becomes a page path: "Checkout / Payment" is /checkout-payment. |
| track(name, props?) | method | A moment your code names, with optional properties. |
| identify(id, traits?) | method | Ties this device's sessions to your own user id. Dropped while disabled. |
| tap(xPct, yPct, label?) | method | A tap, as percentages of the screen from 0 to 100, for heatmaps. label names the element, like data-glass-id on the web. |
| onForeground() / onBackground() | method | Lifecycle. Background sends the queue and saves it; foreground continues the session and refreshes the site settings. |
| flush() | method | Sends everything queued now. |
| setDisabled(bool) | method | The run time privacy switch. Disabling clears the queue and stops all sending. |

### React Native

npm

```
npm install @dbrij/glass-native
```

React Native

```
import AsyncStorage from '@react-native-async-storage/async-storage';
import { AppState, Dimensions, Platform } from 'react-native';
import { createGlass } from '@dbrij/glass-native';

export const glass = createGlass({
  siteKey: 'gk_your_site_key',
  platform: { os: Platform.OS === 'ios' ? 'ios' : 'android', device: 'mobile', appVersion: '1.4.2' },
  storage: AsyncStorage,               // getItem / setItem / removeItem
  screen: Dimensions.get('screen'),
  // disabled: true,                   // start switched off until the person agrees
});

AppState.addEventListener('change', (state) => {
  if (state === 'active') glass.onForeground();
  else if (state === 'background') glass.onBackground();
});

// screens from your navigator
const route = navigationRef.getCurrentRoute();
if (route) glass.screen(route.name);

glass.track('signup', { plan: 'pro' });
glass.identify('user_1234', { plan: 'pro' });
glass.tap(48, 88, 'buy-button');
```

CREATEGLASS OPTIONS

|  |  |  |
| --- | --- | --- |
| siteKey* | string | The gk_ key. Any other value leaves the tracker disabled. |
| platform* | { os, device?, appVersion? } | os is "ios" or "android"; device is "mobile" (default) or "tablet"; appVersion is shown as App <version> (up to 20 characters). |
| storage | GlassStorage | Where the visitor id, session and offline queue persist. Memory by default; pass AsyncStorage or similar on a device. |
| endpoint | string | API base. Default https://api.dbrij.com/api/v1. |
| disabled | boolean | Start disabled. |
| screen | { width, height } | Screen size, for context. |

The React Native client also has `visitorId()`, the persistent visitor id (empty until storage has loaded).

### Swift (iOS)

Swift

```
let glass = GlassTracker(siteKey: "gk_your_site_key", appVersion: "1.4.2")
// optional: endpoint:, device: "tablet", disabled: true, defaults: UserDefaults
// on iOS the app lifecycle is observed for you

glass.screen("Checkout")
glass.track("signup", props: ["plan": "pro"])
glass.identify("user_1234", traits: ["plan": "pro"])
glass.tap(xPct: 48, yPct: 88, label: "buy-button")
glass.setDisabled(true)
```

### Kotlin (Android)

Kotlin

```
val glass = GlassTracker(application, GlassTracker.Config(
    siteKey = "gk_your_site_key",
    appVersion = "1.4.2",
    // endpoint = "https://api.dbrij.com/api/v1", device = "tablet", disabled = true,
))
// constructed with an Application, it follows the activity lifecycle for you

glass.screen("Checkout")
glass.track("signup", mapOf("plan" to "pro"))
glass.identify("user_1234", mapOf("plan" to "pro"))
glass.tap(48.0, 88.0, "buy-button")
glass.setDisabled(true)
```

### Flutter

Flutter

```
final glass = GlassTracker(GlassConfig(
  siteKey: 'gk_your_site_key',
  platformOs: Platform.isIOS ? 'ios' : 'android',
  appVersion: '1.4.2',
  storage: PrefsStorage(prefs), // optional; memory only by default
));

// lifecycle, from a WidgetsBindingObserver
void didChangeAppLifecycleState(AppLifecycleState state) {
  if (state == AppLifecycleState.resumed) glass.onForeground();
  if (state == AppLifecycleState.paused) glass.onBackground();
}

// screens, from a NavigatorObserver
class GlassNavObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route? previous) {
    final name = route.settings.name;
    if (name != null) glass.screen(name);
  }
}
```

`GlassStorage` is three async methods, `getItem`, `setItem` and `removeItem`. Back it with shared preferences to keep the visitor id and the offline queue across launches.

### How the native trackers behave

- Events go in batches of 20, or every 5 seconds, and at once when the app goes to the background.
- Offline, the queue is saved (at most the last 500 events) and sent on the next attempt or launch. The collector replaces any time more than 5 minutes away from its own clock with the time the event arrived.
- A session ends after 30 minutes without activity or 4 hours in total.
- The site settings are read at start and at most every 5 minutes on return to the foreground. When they say the site is unknown or paused, the tracker stops sending.
- No advertising id and no fingerprinting: only the screens and events you name, and a random visitor id.
- Every entry point is wrapped; the tracker never throws into your app.

## Collector API

The trackers above speak this protocol, and you only need it to write a tracker of your own. It runs on other people's pages, which shapes it: no credentials, no custom headers, and bodies sent as `text/plain`, so a browser never sends a preflight. Every response carries `Access-Control-Allow-Origin: *`. Refusals are silent: the collector answers with success and drops what it will not keep.

Base`https://api.dbrij.com/api/v1/public/glass`

### Read the site's tracker settings

GET`/public/glass/config?k=<site key>`

What the tracker should do. JSON, inside the usual `{ "success": true, "data": … }` envelope. 30 requests a minute from one address.

Response data

```
{
  "ok": true,                 // false: unknown key or paused site. Stop sending.
  "sampleRate": 100,          // percent of sessions to record
  "maskingLevel": "balanced", // strict | balanced | relaxed
  "replayEnabled": true,
  "quotaExceeded": false,     // true: the month's events are spent. Do not record.
  "sessionTimeoutMs": 1800000
}
```

### Send events

POST`/public/glass/ingest`

One envelope of events as JSON in a `text/plain` body. Answers `202` with `{ "ok": true }` whatever happened. 120 requests a minute from one address.

Envelope

```
{
  "k": "gk_your_site_key",
  "v": "gv_3f9a0c1d2e4b5a6978c0d1e2",      // visitor id
  "s": "gs_8b7c6d5e4f3a2b1c0d9e8f7a",      // session id
  "url": "https://acme.com/pricing?utm_source=news",
  "ref": "https://news.example/today",
  "utm": { "s": "news", "m": "email", "c": "october" },
  "sw": 1440, "sh": 900,
  "events": [
    { "t": "pageview", "p": "https://acme.com/pricing", "ts": 1767225600000, "props": { "scrollPct": 64 } },
    { "t": "click", "p": "https://acme.com/pricing", "ts": 1767225603000,
      "props": { "selector": "[data-id=\"plan-pro\"]", "x": 812, "y": 1420, "vw": 1440, "vh": 3200, "text": "Choose Pro" } },
    { "t": "custom", "n": "signup", "p": "https://acme.com/pricing", "ts": 1767225610000, "props": { "plan": "pro" } },
    { "t": "identify", "n": "user_1234", "ts": 1767225611000, "props": { "plan": "pro" } }
  ]
}
```

ENVELOPE FIELDS

|  |  |  |
| --- | --- | --- |
| k* | string | Site key, starting gk_, up to 40 characters. |
| v* | string | Visitor id: 10 to 64 letters, digits, underscores or hyphens. |
| s* | string | Session id: 10 to 40 letters, digits, underscores or hyphens. |
| url | string | The page address, up to 600 characters. Used for any event without its own p. |
| ref | string | Referrer, up to 300 characters. A referrer on the same host counts as navigation, not as a source. |
| utm | { s, m, c } | Campaign source, medium and name, up to 120 characters each. |
| sw, sh | number | Screen width and height. |
| pf | "ios" \| "android" | Native trackers only. Any other value is a web envelope. |
| dv | "mobile" \| "tablet" | Native trackers only. Anything but "tablet" reads as mobile. |
| av | string | Native app version, up to 20 characters. |
| events* | Event[] | Up to 50; anything after the 50th is ignored. |

EVENT FIELDS

|  |  |  |
| --- | --- | --- |
| t* | "pageview" \| "click" \| "custom" \| "identify" | Any other type is dropped. |
| n | string | The custom event name (up to 120 characters) or the identify id (up to 200). Required for custom and identify; without it the event is dropped. |
| p | string | The page address, up to 600 characters. Kept as its host and path: query and hash removed, a trailing slash trimmed, the path cut to 300 characters. |
| ts | number | Milliseconds since the epoch. More than 5 minutes from the collector's clock, it is replaced with the time of arrival. |
| props | object | Up to 4 KB of JSON. Larger, or not a plain object, and the props are dropped while the event is kept. Clicks carry selector, x, y, vw, vh, text, rage and dead; page views carry scrollPct. |

A body over 512 KB, a body that is not JSON, or a bad key, visitor id or session id drops the whole envelope. A bad event drops only that event.

### What is dropped without a word

- Requests whose user agent looks automated: bot, spider, crawl, preview, monitor, headless, lighthouse, pingdom or uptime.
- An unknown site key, or a paused site.
- A request whose `Origin` (or, without one, `Referer`) host is not one of the site's allowed domains. A request with neither header is accepted.
- Everything, once the company has used the month's events, or while Dbrij has paused collection or a daily ceiling has been reached for the company.

### Send a replay chunk

POST`/public/glass/replay?k=&s=&q=&st=&et=&c=`

One chunk of recorder events as raw bytes: a JSON array, gzip compressed when `c=1`. The server stores it exactly as received and never inflates it. Answers `204`. 240 requests a minute from one address.

QUERY

|  |  |  |
| --- | --- | --- |
| k* | string | Site key. |
| s* | string | Session id, as in the envelope. |
| q* | integer | Chunk sequence number, 0 to 5000. A sequence number already stored for the session is ignored, so a retry is safe. |
| st, et | integer | Timestamps of the first and last event in the chunk. |
| c | "1" \| "0" | 1 when the body is gzip. |

Chunks over 10 MB, chunks for a site with replay switched off, and chunks after the month's events are spent are dropped.

## Site settings

Each site under Glass, Sites carries the settings below. They decide what every tracker on that site does, wherever it is installed.

|  |  |  |
| --- | --- | --- |
| Name | 1 to 120 characters | How the site reads in the dashboard. |
| Allowed domains | up to 20 | Hosts allowed to send events, such as acme.com. A domain also allows its subdomains. The scheme, www. and any path are removed as you save. Empty accepts any origin, which is fine while you get started. |
| Masking level | strict \| balanced \| relaxed | How much text a replay masks. Default balanced. See Privacy and masking. |
| Replay | on \| off | Whether sessions record at all. Default on. |
| Replay sample rate | 0 to 100 | The percentage of sessions that record. Drawn once per session, so a reload keeps recording. Default 100. |
| Event retention | 7 to 365 days | How long events are kept. Default 90. |
| Replay retention | 1 to 90 days | How long replays stay watchable. Default 30, and never longer than the plan allows (see Plans and limits). |
| Status | active \| paused | Paused tells every tracker to stop, and drops anything that still arrives. |
| Rotate key | action | Issues a new gk_ key. The old one stops being accepted; update your tag and apps. |

### How quickly a change reaches visitors

The collector reads site settings at most a minute old. A web tracker reads them on every page load (keeping a copy for up to 5 minutes) and again every 5 minutes; a native tracker on start and on return to the foreground, at most every 5 minutes.

## Plans and limits

Glass is sold as a small ladder. Each rung adds something rather than only counting higher, and nothing that protects your visitors is ever left to a higher rung: input masking is on every plan. Rungs marked yearly are billed once a year, at ten times the monthly price.

| Plan | Price | Billing | Sites | Events a month | Replays kept | Funnels | Retention cohorts |
| --- | --- | --- | --- | --- | --- | --- | --- |
| One site | ₦1,500 a person a month | Yearly | 1 | 100,000 | 14 days | No | No |
| Team | ₦2,250 a person a month | Yearly | 3 | 400,000 | 30 days | Yes | No |
| Business | ₦3,000 a person a month | Monthly or yearly | Up to 20 | 1,000,000 | 90 days | Yes | Yes |

### The monthly event allowance

The allowance counts every event across all of a company's sites in a calendar month (UTC). Past it, the collector drops new events and replay chunks until the month turns: it is a stop, never a bill. Trackers are told `quotaExceeded`: a web tracker stops recording, and the app trackers stop sending until a later check, when the app comes back to the foreground, finds the site open again. The account is warned as the month's usage nears the allowance.

### Collector limits

|  |  |  |
| --- | --- | --- |
| Events per envelope | 50 | Further events in the same envelope are ignored. |
| Envelope size | 512 KB | Larger envelopes are dropped. |
| Event props | 4 KB of JSON | Larger props are dropped; the event is kept. |
| Clock skew | 5 minutes | Times further off become the time of arrival. |
| Replay chunk | 10 MB | Up to 5,000 chunks per session. |
| Replay per session | 60 minutes | Recording pauses after 3 idle minutes. |
| Session | 30 minutes idle, 4 hours total | A new session starts after either. |
| Requests a minute, one address | 30 / 120 / 240 | Settings / events / replay chunks. |

## SDKs and packages

Every SDK and package for Glass, with what it does, how to install it and the smallest working use. The whole catalogue, for every product, is on [the apps page](https://dbrij.com/apps#sdks).

- TypeScript 1 package
- JavaScript 1 package
- Browsers 1 package
- React Native 1 package
- Swift 1 package
- iOS 1 package
- Kotlin 1 package
- Android 1 package
- Dart 1 package
- Flutter 1 package

### glass.js

The Glass tracker for websites: page views, clicks, custom events and session replay from one script tag.

Script for your site · JavaScript · Served by Dbrij · Runs on Browsers

- Sends events in batches every 5 seconds or at 20 waiting, and once more when the page closes
- Follows route changes in single page apps through the history API
- Replay is recorded by a second file, fetched only for sessions the sample picks
- No cookies and no fingerprinting; Global Privacy Control turns off replay and identify
- glass.track and glass.identify for moments your code names
- Never breaks the page: every part is wrapped and fails silently

Install

```
<script src="https://glass.dbrij.com/glass.js" data-site="gk_your_site_key" defer></script>
```

Quick start

```
<script src="https://glass.dbrij.com/glass.js" data-site="gk_your_site_key" defer></script><script>  // once the tag has loaded  glass.track('signup', { plan: 'pro' });  glass.identify('user_1234', { plan: 'pro' });</script>
```

### @dbrij/glass-native

Glass analytics for React Native: sessions, screens, taps and events in the same dashboard as your website.

SDK · TypeScript · npm · Runs on React Native, Any runtime with fetch

- screen, track, identify and tap, with hooks for the app going to the foreground or background
- Batches 20 events or every 5 seconds, and flushes when the app goes to the background
- An offline queue of up to 500 events, sent on the next launch
- Stops sending when the monthly quota is used up or the site is paused
- No cookies, no fingerprinting and no replay; setDisabled stops and clears everything
- Never throws into your app

Install

```
npm install @dbrij/glass-native
```

Quick start

```
import AsyncStorage from '@react-native-async-storage/async-storage';import { AppState, Dimensions, Platform } from 'react-native';import { createGlass } from '@dbrij/glass-native';export const glass = createGlass({  siteKey: 'gk_your_site_key',  platform: { os: Platform.OS === 'ios' ? 'ios' : 'android', device: 'mobile', appVersion: '1.4.2' },  storage: AsyncStorage,  screen: Dimensions.get('screen'),});AppState.addEventListener('change', (state) => {  if (state === 'active') glass.onForeground();  else if (state === 'background') glass.onBackground();});glass.track('signup', { plan: 'pro' });
```

### glass-tracker.swift

The Glass tracker for native iOS apps, as one Swift file you drop into your project.

SDK · Swift · Served by Dbrij · Runs on iOS, Other Swift platforms with Foundation

- One file with no dependencies beyond Foundation
- On iOS it follows the app lifecycle for you: flushes on background, refreshes on foreground
- screen, track, identify and tap, the same calls as every Glass tracker
- A random visitor id in UserDefaults; no advertising id and no fingerprinting
- Batches of 20 or every 5 seconds, with a queue that survives being offline

Install

```
curl -O https://dbrij.com/glass-tracker.swift
```

Quick start

```
let glass = GlassTracker(siteKey: "gk_your_site_key", appVersion: "1.4.2")glass.screen("Checkout")glass.track("signup", props: ["plan": "pro"])glass.identify("user_1234", traits: ["plan": "pro"])glass.tap(xPct: 48, yPct: 88, label: "buy-button")
```

### GlassTracker.kt

The Glass tracker for native Android apps, as one Kotlin file you drop into your project.

SDK · Kotlin · Served by Dbrij · Runs on Android

- One file using only the Android SDK
- Built with an Application, it follows the activity lifecycle for you
- screen, track, identify and tap, the same calls as every Glass tracker
- All work on one background thread, and nothing ever throws into your app
- A random visitor id in SharedPreferences; no advertising id and no fingerprinting

Install

```
curl -O https://dbrij.com/GlassTracker.kt
```

Quick start

```
val glass = GlassTracker(application, GlassTracker.Config(    siteKey = "gk_your_site_key",    appVersion = "1.4.2",))glass.screen("Checkout")glass.track("signup", mapOf("plan" to "pro"))glass.identify("user_1234", mapOf("plan" to "pro"))glass.tap(48.0, 88.0, "buy-button")
```

### glass_tracker.dart

The Glass tracker for Flutter apps, as one Dart file you drop into lib.

SDK · Dart · Served by Dbrij · Runs on Flutter, Dart with dart:io

- One file using only dart:io and dart:convert
- screen, track, identify and tap, the same calls as every Glass tracker
- Wire the lifecycle with a WidgetsBindingObserver and screens with a NavigatorObserver
- Memory by default; back GlassStorage with shared preferences to keep the queue across launches

Install

```
curl -O https://dbrij.com/glass_tracker.dart
```

Quick start

```
final glass = GlassTracker(GlassConfig(  siteKey: 'gk_your_site_key',  platformOs: Platform.isIOS ? 'ios' : 'android',  appVersion: '1.4.2',));glass.screen('Checkout');glass.track('signup', {'plan': 'pro'});glass.identify('user_1234', {'plan': 'pro'});
```
