Identify users & custom events

Two browser-side calls let you teach Trace more about a visitor: identify() ties an email to the visitor cookie, and track() records funnel events like add-to-cart or quiz-completed.

Why identify users?

Identify is the email-fallback bridge. If a visitor signs up, takes a quiz, or claims a discount, you call identify(email). When a Stripe charge later arrives without trace_visitor_id in metadata — for example, because the customer paid through a Payment Link or the checkout email differs — Trace can still match it back to the original click via that email.

Identify early, identify often
The earlier in the funnel you identify, the more attribution coverage you get. Email capture forms, quiz endings, account creation, and newsletter signups are all good moments.

identify(email, traits?)

js
window.trace.identify('alex@example.com', {
  name: 'Alex',
  plan: 'newsletter',
  signed_up_via: 'quiz'
});

Both arguments are simple — an email string and an optional traits object. Traits are stored on the identity table and surfaced in the dashboard customer drilldown.

When to call it

  • Account signup / registration handler
  • Newsletter form submit
  • Email-required quiz or lead-magnet
  • Logged-in pageviews — call once on session start

What it does, behind the scenes

identify() sends a payload to POST /api/v1/collect with type identify. Trace upserts a row in the identity table keyed by (site_id, visitor_id), storing the email lowercased.

Later, when a charge ingests, the attribution resolver queries identity by email and uses the most recent visitor_id that claims that email.

track(name, props?)

js
window.trace.track('Add to cart', {
  product_id: 'sku_001',
  variant: 'unflavored-90ct',
  price: 39.0,
  quantity: 1
});

window.trace.track('Checkout started', {
  cart_value: 78.0
});

window.trace.track('Quiz completed', {
  result: 'sleep-stack',
  questions_answered: 7
});

Custom events are stored on the event table and stream into the Live events feed in your dashboard. A dedicated funnel view is on the roadmap — for now, the events are queryable but not visualized as a conversion funnel. The first argument is a free-text event name; the second is a flat propsobject. Keep the event name set small and stable — "Add to cart" and "Added To Cart" are treated as different events.

Naming conventions we use internally

  • Past-tense, sentence-case verbs: Quiz completed, Subscribed, Signed up
  • One canonical name per event. Don't mix add_to_cart and Add to cart.
  • Reserve props for things you'll filter or sum on later (price, product_id, count). Don't dump entire objects in there — keep it scalar.

Doing it server-side

If you don't want to wait for the browser to call identify() (e.g. server-rendered checkout completion), POST directly:

ts
// On your server, after a successful signup:
await fetch('https://tracerev.com/api/v1/collect', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    site_id: 'site_xxx',
    visitor_id: cookies.get('trace_visitor_id'), // pull from request cookies
    type: 'identify',
    email: 'alex@example.com',
    traits: { plan: 'standard' },
    ts: Date.now(),
  }),
});

This is the same endpoint the pixel calls. No auth required — only the site_id + a valid visitor_id.

Attribution coverage in practice

Three layers, ranked by accuracy:

  1. Metadata via SDK. Deterministic — the visitor ID is on the charge itself. Use this everywhere you control the Checkout Session.
  2. Email match via identify(). Works whenever the checkout email matches an email we've already seen via identify(). Loses precision when customers use different emails for newsletter vs. checkout.
  3. Unattributed.Last resort — revenue still shows, channel doesn't.

Your attribution_method column on the revenue table tags each row as one of metadata, email_match, or unattributed. If you see your unattributed share creeping up, that's usually a sign the SDK is missing from a checkout path — see troubleshooting.