diff --git a/src/app/users/after-sign-in/route.tsx b/src/app/users/after-sign-in/route.tsx index 60ead413cb..a241a2912a 100644 --- a/src/app/users/after-sign-in/route.tsx +++ b/src/app/users/after-sign-in/route.tsx @@ -1,10 +1,27 @@ import { getProfileRedirectPath, getUserFromAuth } from '@/lib/user.server'; import { isValidCallbackPath } from '@/lib/getSignInCallbackUrl'; import { maybeInterceptWithSurvey } from '@/lib/survey-redirect'; +import PostHogClient from '@/lib/posthog'; import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; import { APP_URL } from '@/lib/constants'; +/** + * Resolves a product identifier from the signup entry point. Returns null when + * the entry point is generic (e.g. /get-started, /profile) so we leave the + * property unset rather than guessing. + */ +function resolveSignupProduct(callbackPath: string | null, hasSource: boolean): string | null { + if (hasSource) return 'kilo-code'; // IDE install flow + if (!callbackPath) return null; + if (callbackPath.startsWith('/claw')) return 'kiloclaw'; + if (callbackPath.startsWith('/cloud')) return 'cloud-agent'; + if (callbackPath.startsWith('/code-reviews')) return 'code-reviews'; + if (callbackPath.startsWith('/app-builder')) return 'app-builder'; + if (callbackPath.startsWith('/install')) return 'kilo-code'; + return null; +} + export async function GET(request: NextRequest) { const url = new URL(request.url); const { user } = await getUserFromAuth({ adminOnly: false, DANGEROUS_allowBlockedUsers: true }); @@ -27,6 +44,24 @@ export async function GET(request: NextRequest) { } if (user.has_validation_stytch === null) { + // New user: stamp which product they signed up for. Derived from + // responsePath (the already-validated destination) rather than the raw + // callbackPath query param, so the value cannot be user-tampered. This + // runs exactly once per signup because has_validation_stytch is set + // after account verification completes. + const product = resolveSignupProduct(responsePath, !!url.searchParams.get('source')); + if (product) { + PostHogClient().capture({ + distinctId: user.google_user_email, + event: 'signup_product_attributed', + properties: { + first_product_signup: product, + signup_destination: responsePath, + $set_once: { first_product_signup: product }, + }, + }); + } + // For new users needing verification, only pass callbackPath if explicitly provided. // Otherwise, account-verification will redirect to /get-started by default. if (callbackPath && isValidCallbackPath(callbackPath)) {