Seeing Who Actually Submitted a PostHog Survey Response
Open a PostHog survey's results tab and the response list often shows something like 01930b4e-8f21-... where a name should be, instead of the person who actually answered. That's not a rendering bug you can filter around. PostHog has confirmed it as an open bug. The survey response records the distinct ID at the moment someone submitted, and it keeps showing that ID even after you identify the person or rename their record. Renaming doesn't back-propagate to responses already collected.
The fix isn't in the results page. It's upstream, in whether the person was identified before they answered, and there are two honest ways to get there. Make identification happen earlier in the session, or accept the distinct_id and do the lookup by hand. Below is both, plus the point where doing it by hand every week stops being worth anyone's time.
Why the name goes missing in the first place
PostHog's identity model has two layers. Every event, survey responses included, carries a distinct_id. A person profile only exists once you've called identify() with something that means something, like an email or a user ID, per PostHog's persons documentation. Before that call, distinct_id is a locally generated anonymous string, and events fire under it just fine, they just don't attach to a named person yet.
When identify() finally runs, say at login, PostHog does merge the anonymous history into the identified profile so that querying by either ID surfaces the same person, according to PostHog's identify docs. That merge is real and it works for analytics. What it doesn't do is rewrite the label already sitting on a specific survey response row, which is the exact gap the open GitHub issue describes. So the common sequence that produces an anonymous-looking survey response goes like this. Someone lands on your site, a survey triggers before they log in or before your app calls identify(), they answer honestly, and the response is now permanently stamped with the pre-login distinct_id, cosmetically, even though the backend later knows exactly who they are.
Step 1: identify people before the survey can trigger, not after
The actual fix is to move your identify() call earlier than survey exposure, not to chase the display after the fact. PostHog's survey targeting supports showing a survey only when a chosen person property is set, so if your app calls identify() with an email at login, you can gate the survey on that property existing. That guarantees every response that shows up going forward is tied to a distinct_id you've already identified, and the name in the results table will be correct from that point on.
This does nothing for the historical rows you already have. Anything collected before you tightened the targeting is stuck with whatever distinct_id it captured, per the bug report.
Step 2: for existing responses, resolve the distinct_id by hand
For the backlog, the workaround is a manual lookup, not a setting:
- Copy the distinct_id off the response row. It's the string shown in place of a name.
- Search PostHog's Persons tab for that ID. Persons can be searched by identifier, and the profile that comes back carries whatever properties got set on it since, including a name or email set later via
identify()orsetPersonProperties(). - Cross-check the timestamp. If the person profile shows an email that was added after the survey date, that's the confirmation you're looking at the right merged history and not a coincidental ID collision.
This works. It also doesn't scale past a handful of responses a week, because it's a manual lookup per row, and it tells you a name, not the account or the support history behind that name.
A quarterly NPS push through PostHog surveys at Kepler Systems, a field-service scheduling tool, pulled in 140 responses, and about a third came back with distinct_ids instead of names, mostly from a batch of surveys shown on the homepage widget before signup. Wes Okonkwo, who runs growth there, raised it in the team's weekly review, pointing out that forty-something of the responses were just UUIDs and resolving each one by hand through Persons was an hour nobody had three times a quarter. The support lead pushed back on writing them off, though, some of those same UUIDs were probably the people who'd filed tickets last month about the mobile scheduling view, and matching them would show whether the detractors were the same accounts already flagged as churn risks.
Wes spent the afternoon manually resolving 40 IDs through the Persons tab. Eleven resolved to accounts already on the churn-risk list from the support queue, information that existed nowhere near the survey results page and that nobody would have connected without the manual cross-reference.
The ceiling on doing this by hand
The Persons-tab workaround holds up for occasional review of a small batch. It stops holding up at a predictable point:
- Volume. A one-off lookup per anonymous response is fine at ten rows a quarter and unworkable at a hundred a month.
- It only returns a name, not the account. Knowing a distinct_id resolves to
wes@keplersystems.iodoesn't tell you Kepler Systems already has an open support ticket about the same feature the survey was asking about. - It depends on PostHog's own identify() hygiene being clean. If your team never calls
identify()with a stable ID for a given user, or calls it inconsistently across devices, there's no profile to resolve to, and the lookup dead-ends regardless of how carefully you search.
That third point is the real ceiling. The lookup only works when PostHog's own identity graph is complete, and for a lot of teams it isn't, because identify() gets called on some surfaces and skipped on others.
That's the point where teams stop trying to fix identity resolution inside PostHog and instead resolve identity somewhere that isn't dependent on PostHog's identify() state at all. We build Modem for that case, so weigh this section accordingly. Modem's PostHog integration queries product analytics, feature flags, experiments, and error tracking; it doesn't read survey text, and PostHog surveys aren't part of what it pulls. What Modem does instead is build its own person and company records from Slack, support tickets, sales calls, and CRM data, independent of PostHog's identity graph. So once you've resolved a distinct_id to an email through the Persons tab, as in step 2 above, that email is usually already sitting on an account record Modem assembled from support history and Slack threads. The manual lookup that used to end at "this is wes@keplersystems.io" ends instead at the account's open tickets and sales history, without a second search.
This doesn't fix PostHog's UI bug, and it isn't meant to; that's PostHog's fix to ship. It solves the actual problem underneath it, which is that a name by itself was never going to tell Wes whether that detractor was a customer worth calling. The same "who is this, really" question shows up in open-text survey answers too; how to analyze open-text PostHog survey responses at scale covers turning two hundred "why" answers into counted themes.
Try it on last quarter's responses
Gate your PostHog surveys on a person property that only exists post-identify, so new responses stop arriving anonymous. Then pull the distinct_ids from your last quarter's results, resolve the worst dozen through the Persons tab, and check whether any of them match an open support ticket. If they do, that's the same signal Wes found, sitting one tab away from where the survey results already live.
