api v1 DEVELOPER DOCS

Install ew & send events.

One async script, no cookies, no build step. This page covers the snippet, the automatic events, the window.ew API, and the raw /api/v1/collect HTTP contract if you want to send events yourself.

01 · QUICK START

Drop one tag in <head>

Add the snippet to every page you want to measure — ideally in <head> so early errors and Web Vitals are captured. Replace YOUR_SITE_KEY with the key shown on your site's Install tab.

<script async defer
        src="https://ew.decipher-ip.com/static/tracker.js"
        data-site="YOUR_SITE_KEY"></script>

That's it. The tracker is ~3 KB, sets no cookies, and starts recording the first pageview as soon as it loads. Your dashboard's install step flips to verified the moment the first event lands.

02 · SCRIPT ATTRIBUTES

Configure via data-*

All configuration lives on the <script> tag — there is nothing to initialize in JS.

AttributeRequiredDescription
data-site yes Your site key. Without it the tracker logs a warning and does nothing.
data-endpoint no Override the collect URL. Defaults to https://ew.decipher-ip.com/api/v1/collect, derived from the script src.
data-ew-release no Build / release tag (e.g. 1.4.2), attached to every error. Lets the dashboard track which release introduced — or fixed — an issue, and detect regressions.
<script async defer
        src="https://ew.decipher-ip.com/static/tracker.js"
        data-site="YOUR_SITE_KEY"
        data-ew-release="1.4.2"></script>
03 · AUTOMATIC TRACKING

What you get for free

Once the snippet loads, ew tracks the following with no extra code:

  • Pageviews — on load, and on SPA navigation (it hooks history.pushState / replaceState and popstate). Duplicate paths are de-duplicated.
  • Engagement — a once-per-second heartbeat separates active time (tab visible) from idle, so reported durations mean real attention.
  • Web Vitals — TTFB, FCP, LCP, CLS and INP, each rated good / needs-improvement / poor.
  • Errors — uncaught exceptions and unhandled promise rejections, with stack traces and breadcrumbs.
  • Clicks — only on elements you opt in with data-ew (see below).
Events are queued, batched and gzipped, then flushed on a timer, on size thresholds, and on page hide via sendBeacon — so almost nothing is lost when a visitor leaves. Errors flush near-immediately.
04 · CLICK TRACKING

Track clicks with data-ew

The tracker does not record every click. Add data-ew="<name>" to any element you care about; the value becomes the event name. Any extra data-ew-* attributes ride along as properties.

<button data-ew="signup_click"
        data-ew-plan="pro"
        data-ew-placement="hero">
  Start free trial
</button>

A click on that button sends a click event named signup_click with props { plan: "pro", placement: "hero" }. Clicks bubble, so the attribute can sit on a wrapper too.

05 · CUSTOM EVENTS

ew.track(name, props)

For anything not tied to a click — a form submit, a purchase, a feature used — call ew.track. The name is capped at 120 chars; props is any JSON-serialisable object.

window.ew.track("signup", { plan: "pro", trial: true });
window.ew.track("purchase", { value: 49, currency: "USD" });

These power goals and funnels: define a goal against a custom event name in the dashboard and conversions start counting.

Timing: the snippet is async, so guard early calls — window.ew && window.ew.track(…) — or call from user interactions, by which point ew is defined.
06 · ERROR TRACKING

Capture handled exceptions

Uncaught errors and rejections are captured automatically. For errors you catch yourself, report them with ew.captureError so they still reach your grouped issues.

try {
  await checkout(cart);
} catch (err) {
  window.ew.captureError(err, { step: "checkout", cartSize: cart.length });
  showFallbackUI();
}

Errors carry the last 20 breadcrumbs (navigation, clicks, console.error calls) and, if set, the data-ew-release tag. Identical errors are throttled client-side (max 5 per fingerprint per minute) so one broken loop can't flood your dashboard. Server-side they're grouped by fingerprint into a single issue.

07 · JS API

window.ew

The global is available once the script has run.

MethodDescription
ew.track(name, props)Send a custom event.
ew.pageview()Manually record a pageview for the current path (de-duplicated against the last one). Rarely needed — SPA navigation is auto-tracked.
ew.captureError(err, extra)Report a caught error with optional extra props.
ew.flush()Force the queued batch to be sent now instead of waiting for the next timer / threshold.
08 · HTTP API

POST /api/v1/collect

The tracker is just a client for this endpoint — you can post events from a server, a native app, or anywhere else. It accepts a single JSON object with a batch of events.

Request

  • POST https://ew.decipher-ip.com/api/v1/collect
  • Body: JSON. May be gzipped with Content-Encoding: gzip (or content type application/x-ew-batch+gzip).
  • No auth header — the k (site key) in the body identifies the site. Requests must come from an allowed origin for that site.
  • Limits: ≤ 64 KB compressed body, ≤ 100 events per batch.
{
  "k":   "YOUR_SITE_KEY",
  "cid": "b1d4…",                 // stable client id (uuid) you generate & persist
  "sent_at": 1733650000000,
  "events": [
    {
      "kind": "pageview",
      "sid":  "9f2c…",            // session id (uuid), stable per visit
      "ts":   1733649999000,      // event time, epoch ms
      "url":  "https://example.com/pricing",
      "ref":  "https://google.com/",
      "lang": "en-US",
      "sw":   1920, "sh": 1080
    },
    {
      "kind": "custom",
      "sid":  "9f2c…",
      "ts":   1733650000000,
      "url":  "https://example.com/pricing",
      "name": "signup",
      "props": { "plan": "pro" }
    }
  ]
}
A single event (no events array) is also accepted: put k, cid and the event fields at the top level.

Response

StatusMeaning
204Accepted (also returned for OPTIONS preflight, DNT: 1, and even on internal ingest errors so the client never retries-storms).
400Invalid JSON, or missing k / cid.
403Origin not allowed for this site.
404Unknown or inactive site key.
413Body too large or bad encoding.
429Rate limited (per client IP).
09 · EVENT KINDS

The kind field

Every event has a kind. These are the values the ingest pipeline understands:

kindNotable propsSent by
pageviewauto (load + SPA nav)
clickname, propsauto (data-ew elements)
customname, propsew.track()
errorname, message, stack, breadcrumbs, releaseauto + ew.captureError()
vitalname (LCP…), value, rating, pathauto
heartbeatactive_ms, idle_ms, pathauto (every 10s)
engagementduration_ms, pathauto (on page leave)
10 · PRIVACY

Cookieless by design

  • No cookies are set. The client id lives in localStorage, the session id in sessionStorage — both first-party, with in-memory fallbacks when storage is blocked.
  • No third-party scripts are loaded; events go only to your collect endpoint.
  • Visitors sending DNT: 1 (Do Not Track) are dropped at the endpoint with a 204 — nothing is stored.