State of the conversion tracking
Today I’m going to talk about the state of all the conversion tracking.
Will discuss what we have in place, how we got here, and what the goal is going forward. With the CDP.
How we got here
- Cursor Pre Merge
- Cursor Merges, Grok Bot Launches
- New challenge discovered
CDP
- Goal
- Why CDP
- How
Current state
- Product surface CDP implementation, what each surface tracks
- Implementation tracker
- The gap: signups, progress over time
- Current attribution: how we measure today, and the role of Conceptual
Going forward
- Click-based attribution: how we build it
Cursor Pre Merge
I joined right around the time the merge happened and when we launched Grok Bot.
Up until that point this is what was in place:
GTM + Server Side GTM Tracking responsible for
- Loading Pixels (the base page view ones) across the networks we were running ads on (TikTok, Google, Facebook, LinkedIn, Reddit, Snapchat)
- Instrumenting GA4
Allan’s PLTV Models
- One row per converting entity (an individual subscriber, a team, or a sales lead), fired once, on the entity’s first paid, non-refunded, non-trial charge — a Stripe paid invoice, or an Apple/Google (and for X Subs, PayPal/X Money) first paid day — across Cursor, Grok, and X Subs.
- The
valueis the predicted 3-year revenue for that entity from its cohort’s retention curve, net of store/payment fees on self-serve rows. - Leads carry a sales expected-contract value extended to 3 years by renewal rate. Each entity is frozen on first materialization and never re-sent, restated, or re-valued.
Conceptual external vendor
We sent our subscriptions through Hightouch to Conceptual’s platform, and Conceptual dispatches those with their enrichment process to the ad networks, and in the ad networks we optimize for those.
Hightouch
The sole focus was around web and so all the above is strictly talking about the web context.
The main event that we were/still today are optimizing for is PLTV for subscriptions.
Notes
- My understanding was that occasionally we would have new events that we would optimize for (Nitesh would have more context on this). For the sake of today I won’t be touching on this.
- Reporting happens through in platform ad network data. View-through based attribution.
Cursor Merges, Grok Bot Launches
My understanding is that with the merge we basically took over paid media across the board, and we went all in on Grok Bot.
Set up on the “xAI side”
- Grok.com (no GA4)
- X.ai (no GA4)
- Both of these had Mixpanel in place we learned later
- And they did have MMP correctly set up for Grok mobile app managed by external agency Twigeo.
For Web the following was changed
- Switched ad accounts for certain platforms, made changes in Hightouch accordingly
- We included the Grok subscriptions in PLTV (Allan’s work)
- Continued to optimize for those events
Mobile
- We instrumented MMP AppsFlyer across both mobile apps
- Sent the following conversions to AppsFlyer
- Exported AppsFlyer device data into Databricks
- Set up a dedicated sync from Hightouch back into AppsFlyer for PLTV events specifically with the necessary AppsFlyer device information
- Set up PLTV tracking to ad networks from the MMP so those can be used for mobile optimization campaigns.
Conversions sent to AppsFlyer
- Grok app (iOS, Android)
af_complete_registrationaf_loginaf_initiated_checkoutaf_start_trialaf_subscribe/af_purchasesent_3_chat_messagessent_5_chat_messagesrate_limit_hit- Grok Bot app (iOS, Android)
af_complete_registrationaf_loginaf_initiated_checkoutaf_start_trialaf_subscribeaf_purchaseaf_grok_linkedonboarding_completedbot_createdfirst_agent_responseintegration_addedusage_limit_hit- From Hightouch, S2S
plg_<tier>_pltv_36mper Cursor, Grok and X Subs tier,slg_contract_value_36m_lead;af_revenue= the PLTV value, keyed onappsflyer_id
Conceptual
- We decided to move away from Conceptual’s tracking to own our own infrastructure and not rely on third party for something this critical
- So we replaced the dispatches to Conceptual and used our own internal enrichment relying on GA4 on the cursor subscriptions
- Having suboptimal enrichment
New challenge discovered
We send traffic to X.ai
For mobile
We send subscriptions back to AppsFlyer, we let AppsFlyer figure out the matching and match parameters.
For web
Still sending PLTV, however enrichment parameters are suboptimal, because we capture all of the attribution parameters we need inside of x.ai and we currently don’t have a way to stitch this back to the PLTV user that signs up.
Thought long and hard of doing hacky workarounds to accomplish this.
Now:
- the cross domain challenge was introduced
- new product surfaces were brought on (grok bot, grok, etc)
- Scott came on board and asked for click through based attribution models through Theresa and team
Goal
Zooming out a little bit, our goal that I believe everybody is aligned on is:
- Optimize marketing campaigns for actions that take place across all platforms
- Track events
- Track those in platform as is (front or backend)
- Track ‘special’ events. Events that exist in Databricks or an external source and need enriching. Example Subscription PLTV (Stripe + app merchant data enriched by Allan’s PLTV models)
- Enrich those events with match parameters needed by ad networks (Crucial !!)
- Attach consent status towards each of the events
- Track events
- Click-through based attribution models which means:
- Marketing touchpoints (visits) with utm/referrer info attached to single user profiles
- Every single event we want to track attached to single user profiles
- Distribute those touchpoints across the event or action we care about
- Weight channels and analyze performance
- Possibly assign the events we are looking at back to click dates for paid media, attach spend impressions clicks to that so we have our own built
- Lifecycle marketing for events
- Enrich CRM with attribution data
- Standardization
- Have all of the above fully standardized so that in the future if there is need for optimizing towards a new event or having attribution for a new event the only bottleneck should be engineering to implement it correctly in the source. Once that’s done if this system is implemented properly those requests should and will be turned around very quickly.
Why CDP
- A CDP captures all visits with attribution and match parameters, instruments identity resolution and provides the ability to send to the destinations either in real time or through reverse ETL
- Identity resolution
- Standardization
- Provides a framework:
Source tracking
Centralize and enforce a clear taxonomy on how events get instrumented across all product surfaces
Enrichment
Centralize and enforce how enrichment models get built both for attribution and conversion tracking
Destination delivery
Send to the destinations either in real time or through reverse ETL
How
- one single user who lands on x.ai with utm’s must be recognized as the same user by the time he signs up for an account wherever he does this.
- that same user if he starts using different products must also be able to be recognized as such.
- Cross device once a user creates an account is easy. That user will sign up with their user_id
Many touchpoints, one user, one value sent back to the ad networks
Cross device: web to app
Product Surface CDP implementation
The seven surfaces that are instrumented today, grouped by where the Hightouch call is made. Click a surface to see what we track there, whether each event is actually landing, and what travels with every event.
Implementation tracker
From the Hightouch Implementation Tracker in Notion. Spec is whether engineering has a written spec; Status is the build.
| Platform | Product | Type | Spec | Status | DRI |
|---|---|---|---|---|---|
| x.ai (cross domain) | Grok | web | Done | QA | Jordi Villa & Pedro Ribeiro |
| grok.com | Grok | web | Done | QA | Jordi Villa & Pedro Ribeiro |
| console.x.ai | Grok | web | Done | QA | Jordi & Adam |
| X AI Backend (Auth) | Grok | backend | Done | QA | Louis Gouirand |
| X AI Subscription Consolidated | Grok | backend | Done | QA live in prod | Sanjib Prabhunandan |
| Grok iOS & Android (native) | Grok | mobile | Done | QA | Adam Śliwakowski |
| Grok Bot iOS & Android (RN) | Grok | mobile | Done | QA | Adam Śliwakowski |
| cursor.com | Cursor | web | Done | In progress | Adeola & Matthew |
The gap: signups
Backend Signed Up (xAI auth service → Hightouch) against Mixpanel’s signup event, distinct users per UTC day, since the backend source went live. Captured means the Mixpanel signup’s user id also has a Signed Up event; every number on this page uses that definition.
Capture rate, headline numbers
Signups per day, thousands of distinct users
Signed Up as % of Mixpanel signups
Oct 7
OneTrust change: opt-out regions load the SDK and fire on first paint. Capture moves from ~21% to ~31%.
Oct 12 to 13
Mixpanel consent-gated. Mixpanel’s own count drops from 166 k to 91 k and Signed Up drops with it. The ratio slips from 31% to 28%; the absolute capture halves.
Oct 14, 14:00 UTC
Signed Up fix on the web path. Capture goes from ~25% to ~50% within the hour, with no change in Mixpanel volume.
Where the rest is
- accounts.x.ai has no browser source, so a signup that never had an x.ai or grok.com cookie carries no anonymous_id
- App sign-in sheets are not stitched yet
- Cursor side: cursor.com SDK in progress; Cursor auth Signed Up is V2
Where every Mixpanel signup went after the Signed Up fix
Oct 14, 15:00 to 20:00 UTC. Every Mixpanel signup is sorted by where it came from, then checked for a Signed Up event. The first bar is everything that has one, from any origin. Each bar below is one origin’s misses, so the bars add up to 100%. Hover or tap a segment for the sub-bucket.
Signups per source, after the fix
Oct 14 15:00 to 20:00 UTC, the same window as the waterfall above. Distinct users per source. Accounts created is the auth DB; Mixpanel is the web account flow; each Hightouch source is its own registration event.
Read across the sources
| Accounts created, excluding X migrations | 92,270 |
| All Hightouch sources, deduplicated, excluding X migrations | 55,721 |
| Hightouch share of accounts created | 60.4% |
| Mixpanel (web account flow) | 19,927 |
| Hightouch Signed Up, also in Mixpanel | 10,451 |
| Web capture, Signed Up / Mixpanel | 52.4% |
| Bot-flagged accounts in the window | 2,097 |
Current attribution: how we measure today
- Currently what we have in place is GA4 on Cursor
- What we have in place on grok.com and x.ai is Mixpanel
- Mixpanel captures all page visits
- Also identifies users at sign up
- We currently do not have anything that reliably stitches those two surfaces together
- All flow into Databricks tables
- We pass hashed user_id’s into GA4
Click-through based attribution: so technically we could stitch subscriptions through user_id
x.ai → grok.com
OK
x.ai / bot → mobile / desktop → web
Grok link checking: technically the sign up happens on accounts.x.ai which would survive the device cookie set by the grok bot visit. Just need to validate the accuracy of this implementation since we inherited this, but can definitely use this for historical analysis.
Sign ups with Cursor
No
cursor.com
Yes, GA4
What role Conceptual plays today
- Conceptual today does not aid us with conversion tracking anymore.
- The enrichment I mentioned means their pixel captures click id’s and cookies on the site, but we still need to implement track/identify calls for them to stitch this to the conversions we also send to them with PLTV (most likely this means that they on their turn use a CDP as well, whitelabeled or something similar).
- They pull data from the network API’s and build dashboards around the conversions we ourselves now send in, so they are a visualization layer effectively.
- We did have our own prior to merging and all account changes, and we asked them to adjust the reporting while we rebuild our own internal reporting, but deprioritized that right now to focus on the other initiatives.
Click-based attribution: how we build it
A short POV on what and how we should build a click based attribution model to supplement our measurement. Three paths, built separately:
1. Web
Visits on x.ai, grok.com, cursor.com through to the web conversion.
2. Web → Mobile
Need to separate the two: a web visit that ends in an app install and an in-app conversion.
3. Ad network → Mobile directly
No web touch at all. The MMP owns this.
1 and 2
- We model sessions from visits with utm info and referrer
- We identify the users, do our identity resolution
- We take the conversions and for each conversion we spread that out through the touchpoints that preceded it
- We have multiple methods of doing this. We can:
- First touch 40%, last touch 40%, distributing the weights in between
- Exclude direct, exclude others; we can have options here
- Build first touch
- Build last touch
- Do this with data science modelling (I have no data science background but Allan or somebody on the team can assist)
- First touch 40%, last touch 40%, distributing the weights in between
- And then distribute that and aggregate
3. Ad network → Mobile
For 3 we use AppsFlyer, which unfortunately is only available in the aggregate (SSOT) for iOS; for Android we have it on the individual level. This is iOS restrictions that just prevent us from capturing it.
iOS
Aggregate only: AppsFlyer SSOT (SKAdNetwork + modelled). No user-level click → install → conversion path, so it joins the model at the aggregate level per campaign.
Android
User-level: appsflyer_id on the install and on every in-app conversion, so it joins identity resolution like any other touchpoint.