From 856b2badf7be5ae9c9f2feb03d5ed83bd2255158 Mon Sep 17 00:00:00 2001 From: Sean Oliver <882952+seanoliver@users.noreply.github.com> Date: Thu, 14 May 2026 13:47:36 -0700 Subject: [PATCH] feat(telemetry): mirror signup_timestamp to PostHog person property (#45951) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature — tiny telemetry addition. +7/-1 in one file. ## What is the current behavior? [PR #45946](https://github.com/supabase/supabase/pull/45946) added `org_count` as a PostHog person property to unblock targeting on org membership. But `org_count` is set on every authenticated session (the `Telemetry` component fires identify whenever `user.id` + `organizations` resolve), not just at signup completion. That means flag filters like `person.org_count == 1` match two populations: - Brand-new dashboard signups currently in their first org (intended) - Returning users who happen to have one org and just signed in (not intended) For the upcoming `dataApiRevokeOnCreateDefault` experiment, this contaminates the activation comparison because the "returning single-org" group can't activate (they already did, months ago), diluting the measured effect. ## What is the new behavior? Adds `signup_timestamp: user.created_at` to the existing `posthogClient.identify` call in `useTelemetryIdentify`. Since gotrue's `user.created_at` is immutable, the value stays constant across sign-ins — no need for `$set_once` semantics, no race with anonymous activity, no cohort refresh lag. Flag targeting can now combine `person.org_count == 1 AND person.signup_timestamp >= ` to cleanly scope to brand-new signups. ## Testing No new unit tests added — the existing `useTelemetryIdentify` function has no test file, the change is one additional field on an existing call, and the property's correctness is verifiable end-to-end (sign up → check PostHog person record). Adding to the test ticket [GROWTH-854](https://linear.app/supabase/issue/GROWTH-854) for coverage along with the broader posthog-client wrapper tests. ## Additional context Ref: [GROWTH-853](https://linear.app/supabase/issue/GROWTH-853) This is the follow-up gate before the 5% rollout of `dataApiRevokeOnCreateDefault` — without this, the experiment would mix brand-new signups with legacy single-org users on sign-in. ## Summary by CodeRabbit * **Chores** * Enhanced telemetry and analytics data collection for signed-in users by improving user identification tracking and adding signup timestamp information for better analytics insights. [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45951) --- packages/common/telemetry.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/common/telemetry.tsx b/packages/common/telemetry.tsx index 3dc49c6e669..b92aae494cc 100644 --- a/packages/common/telemetry.tsx +++ b/packages/common/telemetry.tsx @@ -446,7 +446,13 @@ export function useTelemetryIdentify(API_URL: string) { ...(anonymousId && { anonymous_id: anonymousId }), }) - posthogClient.identify(user.id, { gotrue_id: user.id }) + // user.created_at is gotrue's immutable signup timestamp — safe to $set on + // every identify because the value never changes per user. Lets flag + // targeting distinguish brand-new signups from returning single-org users. + posthogClient.identify(user.id, { + gotrue_id: user.id, + ...(user.created_at && { signup_timestamp: user.created_at }), + }) } }, [API_URL, user?.id]) }