From c4054e9bd53a27b9cd6763caaf849d3ecf18bfe1 Mon Sep 17 00:00:00 2001 From: anthonyiscoding <142696453+anthonyiscoding@users.noreply.github.com> Date: Wed, 27 May 2026 14:22:27 -0500 Subject: [PATCH 1/4] feat: track website_email_submit in PostHog on email form submit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture a website_email_submit event in PostHog when a user successfully submits the email signup form, mirroring the Common Room notify timing but without the email address — only that a submission happened. Attach the Common Room visitor id (signals-sdk-user-id cookie) as both an event and person property so the two systems can be joined per visitor. Co-Authored-By: Claude Opus 4.7 (1M context) --- website/index.html | 7 ++++--- website/posthog-consent.js | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/website/index.html b/website/index.html index 4a9b343091..2e2bf71d3e 100644 --- a/website/index.html +++ b/website/index.html @@ -6710,7 +6710,7 @@
contact
const meta = document.querySelector('meta[name="iii:mailmodo-form-url"]'); return meta && meta.getAttribute('content') ? meta.getAttribute('content').trim() : ''; } - async function submitToMailmodo(email){ + async function submitToMailmodo(email, formLocation){ const url = getMailmodoFormUrl(); if (!url) return; try { @@ -6722,6 +6722,7 @@
contact
// Mailmodo returns 409 for already-subscribed; treat as success. if (!res.ok && res.status !== 409) throw new Error('mailmodo submission failed'); if (typeof window.iiiNotifyCommonRoomEmail === 'function') window.iiiNotifyCommonRoomEmail(email); + if (typeof window.iiiNotifyPostHogEmailSubmit === 'function') window.iiiNotifyPostHogEmailSubmit(formLocation); } catch (_) { // Swallow — we still mark the form submitted so users aren't blocked. } @@ -6750,7 +6751,7 @@
contact
persistAccessRequest(v); emailForm.classList.add('submitted'); emailInput.value = ''; - submitToMailmodo(v); + submitToMailmodo(v, 'hero'); }); } @@ -7104,7 +7105,7 @@
contact
persistAccessRequest(v); form.classList.add('submitted'); input.value = ''; - submitToMailmodo(v); + submitToMailmodo(v, 'footer'); }); })(); diff --git a/website/posthog-consent.js b/website/posthog-consent.js index ddc5f29848..689cb3741e 100644 --- a/website/posthog-consent.js +++ b/website/posthog-consent.js @@ -61,6 +61,26 @@ }); }; + // Records a successful email-form submission in PostHog. Mirrors the timing of + // iiiNotifyCommonRoomEmail, but intentionally omits the email address — we only + // need to know that a submission happened, not who it was. Where available, we + // attach the Common Room visitor id (its signals-sdk-user-id cookie) so the two + // systems can be joined per visitor. + window.iiiNotifyPostHogEmailSubmit = function (formLocation) { + try { + if (localStorage.getItem(STORAGE_KEY) !== 'accepted') return; + if (!window.posthog || typeof window.posthog.capture !== 'function') return; + var props = { form_location: formLocation || 'unknown' }; + var match = document.cookie.match(/(?:^|;\s*)signals-sdk-user-id=([^;]+)/); + if (match) { + var crId = decodeURIComponent(match[1]); + props.common_room_user_id = crId; + props.$set = { common_room_user_id: crId }; + } + window.posthog.capture('website_email_submit', props); + } catch (_) {} + }; + try { if (localStorage.getItem(STORAGE_KEY) === 'accepted') window.iiiLoadPostHog(); } catch (_) {} From 76c98fe20eb15672c33e0030ca8dcfd5ed6b2f8d Mon Sep 17 00:00:00 2001 From: anthonyiscoding <142696453+anthonyiscoding@users.noreply.github.com> Date: Wed, 27 May 2026 14:27:44 -0500 Subject: [PATCH 2/4] feat: identify PostHog person by Common Room id on email submit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under person_profiles: 'identified_only' a bare $set on an anonymous visitor is dropped, so attach the Common Room visitor id via posthog.identify() instead — this creates the person profile and keys it to Common Room. Still capture website_email_submit so the conversion is recorded even without a Common Room id. No email is sent to PostHog. Co-Authored-By: Claude Opus 4.7 (1M context) --- website/posthog-consent.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/website/posthog-consent.js b/website/posthog-consent.js index 689cb3741e..89c547e917 100644 --- a/website/posthog-consent.js +++ b/website/posthog-consent.js @@ -63,9 +63,14 @@ // Records a successful email-form submission in PostHog. Mirrors the timing of // iiiNotifyCommonRoomEmail, but intentionally omits the email address — we only - // need to know that a submission happened, not who it was. Where available, we - // attach the Common Room visitor id (its signals-sdk-user-id cookie) so the two - // systems can be joined per visitor. + // need to know that a submission happened, not who it was. + // + // Where the Common Room visitor id (its signals-sdk-user-id cookie) is present + // we identify() the person by that id. This is what creates the PostHog person + // profile under person_profiles: 'identified_only' (a bare $set on an anonymous + // visitor would be dropped), and keys the profile to Common Room so the two + // systems can be joined per visitor. The website_email_submit event is captured + // regardless, so the conversion is recorded even without a Common Room id. window.iiiNotifyPostHogEmailSubmit = function (formLocation) { try { if (localStorage.getItem(STORAGE_KEY) !== 'accepted') return; @@ -75,7 +80,7 @@ if (match) { var crId = decodeURIComponent(match[1]); props.common_room_user_id = crId; - props.$set = { common_room_user_id: crId }; + window.posthog.identify(crId, { common_room_user_id: crId }); } window.posthog.capture('website_email_submit', props); } catch (_) {} From 74ac4ca9d59a0f67977630b6689c5bccb4febd90 Mon Sep 17 00:00:00 2001 From: anthonyiscoding <142696453+anthonyiscoding@users.noreply.github.com> Date: Wed, 27 May 2026 15:19:26 -0500 Subject: [PATCH 3/4] feat: identify PostHog person by email on submit, retry Common Room cookie Record the email in PostHog and identify the person by it (the key Common Room also de-anonymizes on), attaching the Common Room visitor id when present so the two systems join per visitor. Common Room writes its signals-sdk-user-id cookie asynchronously, so if it's absent at submit time wait 2s and retry once before recording without it. The submission is always recorded either way, with or without the Common Room id. Co-Authored-By: Claude Opus 4.7 (1M context) --- website/index.html | 2 +- website/posthog-consent.js | 57 +++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/website/index.html b/website/index.html index 2e2bf71d3e..42428750a8 100644 --- a/website/index.html +++ b/website/index.html @@ -6722,7 +6722,7 @@
contact
// Mailmodo returns 409 for already-subscribed; treat as success. if (!res.ok && res.status !== 409) throw new Error('mailmodo submission failed'); if (typeof window.iiiNotifyCommonRoomEmail === 'function') window.iiiNotifyCommonRoomEmail(email); - if (typeof window.iiiNotifyPostHogEmailSubmit === 'function') window.iiiNotifyPostHogEmailSubmit(formLocation); + if (typeof window.iiiNotifyPostHogEmailSubmit === 'function') window.iiiNotifyPostHogEmailSubmit(email, formLocation); } catch (_) { // Swallow — we still mark the form submitted so users aren't blocked. } diff --git a/website/posthog-consent.js b/website/posthog-consent.js index 89c547e917..48d16f6477 100644 --- a/website/posthog-consent.js +++ b/website/posthog-consent.js @@ -62,28 +62,53 @@ }; // Records a successful email-form submission in PostHog. Mirrors the timing of - // iiiNotifyCommonRoomEmail, but intentionally omits the email address — we only - // need to know that a submission happened, not who it was. + // iiiNotifyCommonRoomEmail. // - // Where the Common Room visitor id (its signals-sdk-user-id cookie) is present - // we identify() the person by that id. This is what creates the PostHog person - // profile under person_profiles: 'identified_only' (a bare $set on an anonymous - // visitor would be dropped), and keys the profile to Common Room so the two - // systems can be joined per visitor. The website_email_submit event is captured - // regardless, so the conversion is recorded even without a Common Room id. - window.iiiNotifyPostHogEmailSubmit = function (formLocation) { + // We identify() the person by their email (the cross-system key Common Room + // also de-anonymizes on), which creates the PostHog person profile under + // person_profiles: 'identified_only'. Where the Common Room visitor id (its + // signals-sdk-user-id cookie) is present we attach it too, so the two systems + // can be joined per visitor. That cookie is written asynchronously by Common + // Room's signals.js, so if it's not there yet we wait briefly and retry once + // before recording without it — the submission is always recorded either way. + var CR_COOKIE_RETRY_MS = 2000; + + function iiiReadCommonRoomId() { + var match = document.cookie.match(/(?:^|;\s*)signals-sdk-user-id=([^;]+)/); + return match ? decodeURIComponent(match[1]) : null; + } + + function iiiSendPostHogEmailSubmit(email, formLocation, crId) { try { - if (localStorage.getItem(STORAGE_KEY) !== 'accepted') return; if (!window.posthog || typeof window.posthog.capture !== 'function') return; - var props = { form_location: formLocation || 'unknown' }; - var match = document.cookie.match(/(?:^|;\s*)signals-sdk-user-id=([^;]+)/); - if (match) { - var crId = decodeURIComponent(match[1]); - props.common_room_user_id = crId; - window.posthog.identify(crId, { common_room_user_id: crId }); + var distinctId = email || crId; + if (distinctId) { + var personProps = {}; + if (email) personProps.email = email; + if (crId) personProps.common_room_user_id = crId; + window.posthog.identify(distinctId, personProps); } + var props = { form_location: formLocation || 'unknown' }; + if (email) props.email = email; + if (crId) props.common_room_user_id = crId; window.posthog.capture('website_email_submit', props); } catch (_) {} + } + + window.iiiNotifyPostHogEmailSubmit = function (email, formLocation) { + try { + if (localStorage.getItem(STORAGE_KEY) !== 'accepted') return; + if (!window.posthog || typeof window.posthog.capture !== 'function') return; + var crId = iiiReadCommonRoomId(); + if (crId) { + iiiSendPostHogEmailSubmit(email, formLocation, crId); + } else { + // Common Room may not have written its cookie yet; give it one retry. + setTimeout(function () { + iiiSendPostHogEmailSubmit(email, formLocation, iiiReadCommonRoomId()); + }, CR_COOKIE_RETRY_MS); + } + } catch (_) {} }; try { From e85b38a4f7b86c24d9b0c61de1dec9eb7a7386e5 Mon Sep 17 00:00:00 2001 From: anthonyiscoding <142696453+anthonyiscoding@users.noreply.github.com> Date: Wed, 27 May 2026 15:45:34 -0500 Subject: [PATCH 4/4] refactor: drop deferred Common Room id lookup on email submit Read the signals-sdk-user-id cookie once at submit time and fire identify + capture immediately, instead of waiting 2s and retrying when the cookie is absent. If Common Room hasn't written the cookie yet we simply record without common_room_user_id. Co-Authored-By: Claude Opus 4.7 (1M context) --- website/posthog-consent.js | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/website/posthog-consent.js b/website/posthog-consent.js index 48d16f6477..c5c7937ab6 100644 --- a/website/posthog-consent.js +++ b/website/posthog-consent.js @@ -61,26 +61,22 @@ }); }; - // Records a successful email-form submission in PostHog. Mirrors the timing of - // iiiNotifyCommonRoomEmail. - // - // We identify() the person by their email (the cross-system key Common Room - // also de-anonymizes on), which creates the PostHog person profile under - // person_profiles: 'identified_only'. Where the Common Room visitor id (its - // signals-sdk-user-id cookie) is present we attach it too, so the two systems - // can be joined per visitor. That cookie is written asynchronously by Common - // Room's signals.js, so if it's not there yet we wait briefly and retry once - // before recording without it — the submission is always recorded either way. - var CR_COOKIE_RETRY_MS = 2000; - + // Records a successful email-form submission in PostHog. We identify() the + // person by their email (the cross-system key Common Room also de-anonymizes + // on), which creates the PostHog person profile under + // person_profiles: 'identified_only'. Where Common Room's signals-sdk-user-id + // cookie is already present we attach its visitor id too, so the two systems + // can be joined per visitor; if it isn't set yet we just record without it. function iiiReadCommonRoomId() { var match = document.cookie.match(/(?:^|;\s*)signals-sdk-user-id=([^;]+)/); return match ? decodeURIComponent(match[1]) : null; } - function iiiSendPostHogEmailSubmit(email, formLocation, crId) { + window.iiiNotifyPostHogEmailSubmit = function (email, formLocation) { try { + if (localStorage.getItem(STORAGE_KEY) !== 'accepted') return; if (!window.posthog || typeof window.posthog.capture !== 'function') return; + var crId = iiiReadCommonRoomId(); var distinctId = email || crId; if (distinctId) { var personProps = {}; @@ -93,22 +89,6 @@ if (crId) props.common_room_user_id = crId; window.posthog.capture('website_email_submit', props); } catch (_) {} - } - - window.iiiNotifyPostHogEmailSubmit = function (email, formLocation) { - try { - if (localStorage.getItem(STORAGE_KEY) !== 'accepted') return; - if (!window.posthog || typeof window.posthog.capture !== 'function') return; - var crId = iiiReadCommonRoomId(); - if (crId) { - iiiSendPostHogEmailSubmit(email, formLocation, crId); - } else { - // Common Room may not have written its cookie yet; give it one retry. - setTimeout(function () { - iiiSendPostHogEmailSubmit(email, formLocation, iiiReadCommonRoomId()); - }, CR_COOKIE_RETRY_MS); - } - } catch (_) {} }; try {