Data model
Visitors, users, actors, sessions, conversions, and the full list of columns stored on every event.
This page defines every term LaunchPulse uses and lists every column on an event. If a number somewhere in the app doesn’t match your intuition, the definition is probably here.
Core concepts
| Concept | Definition |
|---|---|
| Visitor | visitor_id, from the _lp_vid cookie. Anonymous, one per browser/device, 30-day lifetime |
| User | user_id, set by identify() with your internal id. The $identify event itself is stored, so journeys stay reconstructable |
| Actor | user_id when set, otherwise visitor_id. The unit for conversion, activation, alert visitor counts, and “people” counts |
| Session | session_id, rotated after 30 minutes of inactivity and by reset() |
| Conversion | A read-time definition: an event name (aliases included) OR a $pageview whose path starts with your configured path. An actor converts at their first matching event |
| First value / activation | An actor is activated when they hit the first-value event at or after their conversion time |
| Time to value | Median seconds between conversion and first value, across activated actors |
| Attribution | First-touch by default. Sources group by first_source, frozen for the visitor’s 30-day lifetime |
Visitor vs. actor — the one that trips people up
The dashboard’s “Visitors” counts distinct visitor ids. But converters and activated count distinct actors.
That difference is deliberate. A visitor id is per browser, so the same person on their laptop and their phone is two visitors. Once they log in and you call identify(), both collapse into one actor. Counting conversions by actor means a person who signs up on mobile and activates on desktop is one converter, not two.
Practical consequence: a conversion rate is not a clean division of two numbers on the same screen. It’s converting actors over the relevant visitor population, and if a lot of your people use multiple devices, actor counts will be lower than visitor counts by design.
Activation is a share of converters
Activation rate is activated ÷ converters, not activated ÷ visitors. Someone who never converted can’t activate, so they aren’t in the denominator.
And activation requires the first-value event at or after the conversion timestamp. If your app fires first value before conversion, that actor never counts as activated. The onboarding test view warns about exactly this inversion — see Going live.
Full detail in Conversions and activation.
Attribution
Attribution is first-touch by default: a visitor’s source is whatever brought them in the first time, frozen for the 30-day cookie lifetime. That’s first_source, first_campaign, first_landing_page, and it’s what the Sources report groups by.
Session-level fields (session_source, session_medium, session_campaign, session_landing_page) are also recorded, so you can break insights down by the source of a specific session rather than the visitor’s original one. See Sources.
Two behaviors that surprise people
1. Configuration is retroactive
Your conversion event, your first-value event, and your aliases are never written into stored events. They’re applied when a query runs.
That means changing any of them reinterprets your entire history instantly. No re-install, no re-tagging, no data migration, no waiting for new data to accumulate. Rename your conversion event and last quarter’s conversion numbers are recomputed on the next page load.
This is why aliases fix typos retroactively, and why it’s safe to change your mind about what counts as activation a month in. See Event aliases.
2. Empty columns disappear
The analytics store drops columns that are entirely empty for a project. If you’ve never sent a value for something, the column doesn’t exist for you.
Three consequences you’ll actually run into:
- Revenue reads 0 if you’ve never sent revenue. Not an error — the column isn’t there. See Revenue tracking.
- An insight broken down by a dimension you never send renders as an empty card. Break down by UTM campaign on a site that has never received a UTM tag and there’s nothing to show.
- The events detail view shows a different column set per project. Two projects, two column lists.
Event columns
Every column you’ll see in the events detail view and in the CSV export.
| Column | Meaning |
|---|---|
time | The event timestamp |
received_at | Authoritative server receive time. Drives partitioning |
client_time | What the browser reported, clamped to [received − 5 min, received] |
event_id | Unique id for the event |
project_id | The project the event belongs to |
event_name | The event name, e.g. $pageview, signup |
visitor_id | Anonymous per-browser id from _lp_vid |
session_id | Session id, rotated after 30 min idle or on reset() |
user_id | Your internal id, set by identify() |
ingest_source | browser or server |
hostname | Host the event came from |
path | Page path |
url | Full page URL |
referrer | Referring URL |
utm_source | utm_source from the URL |
utm_medium | utm_medium from the URL |
utm_campaign | utm_campaign from the URL |
utm_content | utm_content from the URL |
utm_term | utm_term from the URL |
source | Derived source for this event |
medium | Derived medium for this event |
campaign | Derived campaign for this event |
session_source | Source attributed to the session |
session_medium | Medium attributed to the session |
session_campaign | Campaign attributed to the session |
session_landing_page | First page of the session |
first_source | First-touch source, frozen for the visitor’s lifetime |
first_campaign | First-touch campaign |
first_landing_page | First page the visitor ever landed on |
country | ISO-3166 alpha-2 country code |
device | Device type |
browser | Browser |
os | Operating system |
revenue_micros | Revenue × 1,000,000, as an integer |
currency | Currency of the revenue value |
properties | Your custom properties |
Notes on the ones that aren’t self-explanatory:
received_atis the authority. It’s when the collector actually got the event, and it’s what partitioning is based on.client_timeis a claim made by the browser, so it’s clamped to the window [received − 5 minutes, received] — a device with a wildly wrong clock can’t push events into the future or far into the past.revenue_microsis an integer, not a float. $19.99 is19990000. Integer math avoids floating-point drift when summing revenue.propertiesholds whatever you send. Keep personal data out of it — see Privacy.
Project statuses
| Status | Behavior |
|---|---|
setup | Onboarding. Events go to the debug stream only and are never stored |
active | Events are persisted |
paused | Events are accepted and dropped |
| Soft-deleted | Project disappears from the app; analytics data is retained |
Range semantics
How each range in the app is built:
| Range | Window | Buckets |
|---|---|---|
today | Since UTC midnight | Hour |
7d / 30d / 90d | Rolling N × 24 hours | Day |
12mo | From the 1st of the month, 11 months back | Month |
| Custom | Snaps to UTC days, capped at 12 months | Day |
The Local/UTC toggle affects display only. Bucketing is always computed in UTC. Switching to Local relabels the axis; it does not recompute which events fall into which bucket. On a non-UTC timezone this makes labels look offset relative to bucket boundaries — that’s expected, not a bug.
What’s next
- Events log — browsing raw events and exporting CSV.
- Conversions and activation — configuring the funnel.
- Troubleshooting — when a number looks wrong.