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(email, traits?)
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?)
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_cartandAdd to cart. - Reserve
propsfor 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:
// 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:
- Metadata via SDK. Deterministic — the visitor ID is on the charge itself. Use this everywhere you control the Checkout Session.
- 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. - 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.