Back to the growth library
PLGIntegrations

PostHog Signup Enrichment: Feature Flags, Cohorts, and Groups That Actually Know Who Signed Up

How to enrich PostHog person and group properties at signup so feature flag rollouts, cohorts, and session replay filters are based on company fit instead of a blank profile.

Developer reviewing a product analytics dashboard with charts and event data

A PostHog person profile with three properties isn't a person

Open a random user in PostHog after signup and you'll usually see distinct_id, $initial_referrer, and maybe an email if your form collected one. That's it. Every feature flag rollout, cohort, and session replay filter built on top of that profile is working with almost nothing.

PostHog is unusually well suited to fixing this, more than most product analytics tools, because person properties, group properties, feature flags, and cohorts all read from the same identity graph. Enrich a person once and every downstream feature (flag targeting, cohort membership, replay filters, insights) inherits it automatically. Most teams never wire that up. They install the SDK, fire $pageview and a handful of custom events, and leave the person profile blank.

Groful resolves company, role, and ICP fit at signup. This post is about getting that into PostHog specifically: person properties, group properties, and the flag/cohort logic built on top of them. For the enrichment fundamentals, see the PLG signup enrichment guide.

Where PostHog enrichment actually pays off

Feature flag rollouts targeted by fit, not just plan. PostHog flags can target on any person or group property. Roll out a complex feature to icp_fit = "high" accounts first instead of a random 10% of everyone, including the free-tier accounts who'll never use it.

Cohorts that aren't stale the day after you build them. A static cohort built from a CSV export is out of date within a week. A dynamic cohort filtered on company_size_band or icp_confidence updates itself as new signups come in.

Session replay you can actually filter. Watching replays of random signups is a waste of an afternoon. Filtering replays to icp_fit = "high" AND event = "checkout_started" gets you to the sessions worth watching in under a minute.

Insights segmented by fit instead of blended. The same problem shows up in every analytics tool: a funnel or retention chart that mixes your ICP with tire-kickers tells you less than either segment alone would. This is the same logic behind account scoring and PLG activation scoring: a fit label sitting next to the behavioral data, not bolted on after the fact in a spreadsheet.

Wiring enrichment into PostHog

PostHog splits identity into two objects: person properties (via identify) and group properties (via groupIdentify). Get enrichment into both, at signup, before the first real event fires.

Identify the person and the group in the same call

import { PostHog } from "posthog-node";

const posthog = new PostHog(process.env.POSTHOG_API_KEY, {
  host: process.env.POSTHOG_HOST,
});

const enrichment = await fetch("https://api.groful.co/v1/enrichment/lookup", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.GROFUL_API_KEY}`,
  },
  body: JSON.stringify({ externalId: userId, email }),
}).then((res) => res.json());

const domain = enrichment.company?.domain ?? "unknown";

posthog.identify({
  distinctId: userId,
  properties: {
    company_domain: domain,
    company_size_band: enrichment.company?.employeeCountBand ?? "unknown",
    job_title: enrichment.user?.jobTitle ?? "unknown",
    icp_fit: enrichment.company?.icpFit ?? "unknown",
    icp_confidence: enrichment.company?.confidence ?? "low",
  },
});

posthog.groupIdentify({
  groupType: "company",
  groupKey: domain,
  properties: {
    industry: enrichment.company?.industry ?? "unknown",
    employee_count: enrichment.company?.employeeCount ?? null,
    icp_fit: enrichment.company?.icpFit ?? "unknown",
  },
});

posthog.capture({
  distinctId: userId,
  event: "signup_enriched",
  properties: { $groups: { company: domain } },
});

If the enrichment call fails or times out, still call identify() with whatever fields you have and skip the group write rather than blocking signup on a third-party request.

Attach the group to every event that follows

groupIdentify sets the group's properties once. It doesn't retroactively tag events. Any event you want attributed to the company (checkout started, feature used, invite sent) needs $groups: { company: domain } passed in its own properties at capture time. The cleanest way to do this server-side is to wrap your capture calls in a helper that always injects the current user's group key, so nobody forgets it three events into a new feature.

Build flags on top of the enriched properties

Once icp_fit exists on the person object, target a flag directly on it in the PostHog UI: release condition icp_fit equals high, rollout percentage 100%. No code change needed on the flag side; the targeting logic lives entirely in properties you already set at signup.

const showAdvancedOnboarding = await posthog.isFeatureEnabled(
  "advanced-onboarding-flow",
  userId
);

Build the cohort once, reuse it everywhere

A dynamic cohort filtered on icp_fit = high AND company_size_band != "1-10" becomes a single dropdown option across every insight, funnel, and session replay filter in the project. Build it once under Cohorts, name it something a non-engineer can find, and stop rebuilding the same filter combination in every new report.

Where teams get this wrong

Calling identify() on every page load. identify() merges properties, so calling it repeatedly isn't dangerous by itself, but re-running enrichment on every session wastes API credits for data that changes rarely. Cache the enrichment result and refresh it weekly, not per session.

Setting person properties but never calling groupIdentify. These are two different objects in PostHog's data model. A person property lives on the individual; group targeting, group-level insights, and group-based flag conditions all read from the group object instead. Skipping groupIdentify means group-level flag targeting silently has nothing to match against.

Forgetting $groups on capture calls. Group identification without attaching $groups to your events means those events never show up when you filter an insight by company. The group profile exists; it's just disconnected from the activity that should belong to it.

Treating unknown and low as the same value. A signup where enrichment hasn't finished yet and a signup that resolved to a genuinely poor-fit company look identical in a chart unless you keep those states as distinct property values. Collapsing them makes the low-fit segment look bigger than it is and hides how much of your funnel is really "still resolving."

A short rollout plan

Days 1–3: Add the identify() / groupIdentify() pair to the signup flow, confirm both person and group properties populate correctly for a handful of test accounts, and check that new events carry $groups.

Days 4–7: Backfill existing users by looping through your user table, resolving enrichment for anyone missing it, and re-running identify() in a batch job with rate limits against both PostHog and your enrichment credit budget.

Week 2: Build the fit-based cohort, retarget one in-flight feature flag rollout to lead with high-fit accounts, and swap a blended funnel or retention insight for a fit-segmented version.

The identity graph is already there. Most teams just feed it nothing.

PostHog's targeting, cohorts, and replay filters are only as sharp as the properties sitting on the person and group objects behind them. Most implementations leave those objects nearly empty and then wonder why every segment they build feels arbitrary.

Groful resolves company, role, and fit at signup so growth managers get that context into PostHog before the first meaningful event fires, not reconstructed weeks later from a data warehouse join. Review the PLG signup enrichment solution, check pricing, or contact Groful to scope a PostHog integration for your product.

Turn this playbook into workflow

Enrich signups, score ICP fit, and surface expansion opportunities with Groful.