[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-posthog-debugging-surveys":3,"mdc-kq8nbm-key":48,"related-org-posthog-debugging-surveys":2241,"related-repo-posthog-debugging-surveys":2407},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":23,"repoUrl":24,"updatedAt":25,"license":26,"forks":27,"topics":28,"repo":43,"sourceUrl":46,"mdContent":47},"debugging-surveys","debug PostHog surveys","Debug, support, and build PostHog Surveys across the backend and all five SDKs (web\u002Fposthog-js, iOS, Android, Flutter, React Native). Use whenever a Surveys support ticket is pasted (\"survey not showing\", \"fewer responses than expected\", \"responses disappeared\", \"survey shows on wrong platform\"), when diagnosing why a survey does or doesn't display, or when doing survey feature work that must ship across SDKs. Covers the eligibility pipeline, cross-SDK feature parity, the known-cause catalog, read-only diagnostic queries, staff access, and the customer-reply style guide.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"posthog","PostHog","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fposthog.png",[12,16,17,20],{"name":13,"slug":14,"type":15},"Observability","observability","tag",{"name":9,"slug":8,"type":15},{"name":18,"slug":19,"type":15},"Analytics","analytics",{"name":21,"slug":22,"type":15},"Debugging","debugging",35568,"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog","2026-07-01T08:01:43.219597",null,2977,[29,30,19,31,32,33,34,35,36,37,38,39,40,41,42],"ab-testing","ai-analytics","cdp","data-warehouse","experiments","feature-flags","javascript","product-analytics","python","react","session-replay","surveys","typescript","web-analytics",{"repoUrl":24,"stars":23,"forks":27,"topics":44,"description":45},[29,30,19,31,32,33,34,35,36,37,38,39,40,41,42],"🦔 PostHog is an all-in-one developer platform for building successful products. We offer product analytics, web analytics, session replay, error tracking, feature flags, experimentation, surveys, data warehouse, a CDP, and an AI product assistant to help debug your code, ship features faster, and keep all your usage and customer data in one stack.","https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog\u002Ftree\u002FHEAD\u002Fproducts\u002Fsurveys\u002Fskills\u002Fdebugging-surveys","---\nname: debugging-surveys\ndescription: >-\n  Debug, support, and build PostHog Surveys across the backend and all five SDKs\n  (web\u002Fposthog-js, iOS, Android, Flutter, React Native). Use whenever a Surveys\n  support ticket is pasted (\"survey not showing\", \"fewer responses than expected\",\n  \"responses disappeared\", \"survey shows on wrong platform\"), when diagnosing why a\n  survey does or doesn't display, or when doing survey feature work that must ship\n  across SDKs. Covers the eligibility pipeline, cross-SDK feature parity, the known-cause\n  catalog, read-only diagnostic queries, staff access, and the customer-reply style guide.\n---\n\n# Debugging surveys\n\nPostHog Surveys is a no-code in-app form builder. A customer creates a survey in the\nPostHog UI; it must then be evaluated and rendered by whichever SDK their app runs.\n**Most \"survey not showing\" tickets are eligibility problems, not rendering bugs** — the\nSDK correctly decided the user is not eligible, and the job is to find _which_ gate\nfailed and _why_.\n\n## Repos\n\nGitHub is the source of truth for where the code lives. When you need to read or change SDK\nsource, resolve a local checkout via the registry described in\n[references\u002Flocal-repos.md](references\u002Flocal-repos.md) so a clone is found once and reused —\ndon't re-clone every session. First time on a machine, run `python3 scripts\u002Frepos.py init`\nto auto-discover existing checkouts; thereafter `python3 scripts\u002Frepos.py ensure \u003Crepo>`\nprints the path (and `--clone` clones if missing).\n\n| Concern              | Repo                                                                        | Where to look                                                            |\n| -------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------ |\n| Product UI + backend | this monorepo (PostHog\u002Fposthog)                                             | UI: `frontend\u002Fsrc\u002Fscenes\u002Fsurveys\u002F`, backend: `products\u002Fsurveys\u002Fbackend\u002F` |\n| Web SDK              | [PostHog\u002Fposthog-js](https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-js)                 | `packages\u002Fbrowser\u002F`                                                      |\n| React Native SDK     | [PostHog\u002Fposthog-js](https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-js) (same monorepo) | `packages\u002Freact-native\u002F`                                                 |\n| iOS SDK              | [PostHog\u002Fposthog-ios](https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-ios)               | survey rendering + eligibility                                           |\n| Android SDK          | [PostHog\u002Fposthog-android](https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-android)       | eligibility (delegate-based UI)                                          |\n| Flutter SDK          | [PostHog\u002Fposthog-flutter](https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-flutter)       | Dart rendering; native iOS\u002FAndroid handles eligibility                   |\n| Public docs          | [PostHog\u002Fposthog.com](https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog.com)               | `contents\u002Fdocs\u002Fsurveys\u002F`                                                 |\n\nAlways check the local checkout is present and on a sane branch before quoting code; line\nnumbers drift, so grep for the symbol rather than trusting a remembered line number.\n\n## Cross-SDK feature parity (check this FIRST)\n\nA large class of tickets is \"customer expects a feature their platform doesn't support.\"\nConfirm the survey's `lib` \u002F the customer's platform before anything else, then consult\nthis table. Verified against the SDK source — re-verify if it's been months, the gaps\nget filled over time.\n\n| Feature                         | Web (posthog-js)                | iOS                                       | Android                            | Flutter                            | React Native                            |\n| ------------------------------- | ------------------------------- | ----------------------------------------- | ---------------------------------- | ---------------------------------- | --------------------------------------- |\n| Rendering                       | DOM + shadow root               | Native SwiftUI (`SurveysWindow`)          | **No built-in UI** — delegate only | Dart widgets (`SurveyBottomSheet`) | RN components (`SurveyModal`)           |\n| Event-based triggers            | yes (since 1.137.0, 2024-06-05) | yes                                       | yes                                | yes (native side)                  | yes                                     |\n| URL \u002F screen targeting          | yes                             | decoded but **NOT evaluated** (`\u002F\u002F TODO`) | decoded but **NOT evaluated**      | **NOT evaluated** (native gap)     | **explicitly excluded** in filter       |\n| Feature-flag \u002F cohort targeting | yes                             | yes                                       | yes                                | yes (native side)                  | yes                                     |\n| `seenSurveyWaitPeriodInDays`    | yes                             | yes                                       | yes                                | yes (native side)                  | stored but **comparison commented out** |\n| `surveyPopupDelaySeconds`       | yes                             | **not implemented**                       | **not implemented**                | **not implemented**                | **not implemented** (TODO)              |\n\nConsequences worth memorizing:\n\n- **`surveyPopupDelaySeconds` is web-only.** If a mobile ticket blames the delay, it's a red herring.\n- **URL targeting is effectively web-only.** Mobile SDKs decode the field but never enforce it; React Native filters those surveys out entirely. A mobile survey with a URL condition behaves as \"no URL condition\" (mobile\u002Fflutter) or \"never shows\" (RN).\n- **Android ships no survey UI.** The app (or the Flutter plugin) must provide a `PostHogSurveysDelegate`. \"Survey never renders on Android\" is often a missing delegate, not a PostHog bug.\n- **Flutter is hybrid:** triggering\u002Feligibility runs in the native iOS\u002FAndroid layer; rendering is Dart (`SurveyService.showSurvey` → `showModalBottomSheet`). It does _not_ \"just call native\" for UI. So a Flutter rendering bug lives in Dart; a Flutter eligibility bug lives in native.\n- **React Native wait period is silently disabled** (the check is commented out). Don't blame the wait period on RN.\n\nFor a deeper version-by-version capability audit, see the `survey-sdk-audit` skill if available.\n\n## How a survey actually gets shown (the web eligibility pipeline)\n\nThe web SDK is the most complex and the most common in tickets. Mental model from\n`packages\u002Fbrowser\u002Fsrc\u002Fextensions\u002Fsurveys.tsx` (`checkSurveyEligibility`) — checks run in\norder, first failure wins:\n\n1. `isSurveyRunning` — has `start_date`, no `end_date`.\n2. survey `type` is in-app (Popover \u002F Widget \u002F API).\n3. `linked_flag_key` enabled (if set).\n4. `targeting_flag_key` enabled (if set) — customer-defined property targeting.\n5. `_internalFlagCheckSatisfied` — the auto-generated internal targeting flag.\n6. `hasWaitPeriodPassed` — `seenSurveyWaitPeriodInDays` vs `localStorage.lastSeenSurveyDate`.\n7. `getSurveySeen` — per-survey seen flag.\n\nThen in `getActiveMatchingSurveys`: URL\u002Fdevice\u002Fselector match, event\u002Faction trigger fired, and flag re-check.\n\nTwo non-obvious facts that drive real tickets:\n\n- **The server returns ALL non-archived surveys** (`SurveyViewSet`, `products\u002Fsurveys\u002Fbackend\u002Fapi\u002Fsurvey.py`). It does **not** pre-filter by the internal targeting flag. All eligibility is client-side. So you cannot conclude \"the backend excluded them\" — the SDK did.\n- **The wait period has TWO independent implementations.** `canActivateRepeatedly` (true when `schedule: 'always'`) short-circuits `_internalFlagCheckSatisfied` (step 5) — so `always` bypasses the internal flag, including its `$last_seen_survey_date` rule. But `hasWaitPeriodPassed` (step 6) reads `localStorage.lastSeenSurveyDate` directly and is **NOT** bypassed by `canActivateRepeatedly`. So a `schedule: 'always'` survey with `seenSurveyWaitPeriodInDays: 30` still enforces the 30-day wait via the localStorage path. `lastSeenSurveyDate` is updated whenever _any_ survey is shown, regardless of completion.\n\n## Debugging workflow\n\n1. **Parse the ticket.** Extract: org\u002Fproject ID, instance (US vs EU — URLs differ), survey ID(s), the `lib` (platform), the symptom in precise terms, and what the customer already tried. If the ticket is aged or has prior support replies, the config may have been edited mid-thread — treat earlier claims as stale and re-pull current state.\n\n2. **Disambiguate \"none\" vs \"fewer.\"** Customers say \"no responses\" when they mean \"fewer.\" Pull the `survey shown` vs `survey sent` counts before\u002Fafter the suspected change (see [references\u002Fdiagnostic-queries.md](references\u002Fdiagnostic-queries.md)). If the _response rate_ (sent\u002Fshown) is stable, the problem is upstream eligibility (fewer people shown), not rendering or submission. This single check redirects most investigations correctly.\n\n3. **Platform parity check.** Confirm the `lib` and consult the parity table. Eliminate features the platform doesn't support before investigating them.\n\n4. **Pull the survey definition.** `GET \u002Fapi\u002Fprojects\u002F\u003Cid>\u002Fsurveys\u002F\u003Csid>\u002F`. Inspect `conditions` (events, url, seenSurveyWaitPeriodInDays), `appearance.surveyPopupDelaySeconds`, `schedule`, `linked_flag`, `targeting_flag`, `internal_targeting_flag.filters`, `responses_limit`, `iteration_*`.\n\n5. **Pull the targeting-flag activity log** for any \"stopped showing\" ticket. Cohort swaps and rollout changes are invisible in the current config but show up here: `GET \u002Fapi\u002Fprojects\u002F\u003Cid>\u002Factivity_log\u002F?scope=FeatureFlag&item_id=\u003Cflag_id>&limit=20`. Also `?scope=Survey&item_id=\u003Csid>` to see whether the survey itself was edited.\n\n6. **Confirm with events.** Use `$feature_flag_called` to see what the gating flag actually returned for affected users, and whether `$groups` is set (see group-aggregation cause below). Use `survey shown` to see real reach vs the stats UI.\n\n7. **Diagnose against the known-cause catalog**, confirm with one targeted query, then write the reply.\n\n## Known-cause catalog\n\nOrdered roughly by how often they're the answer.\n\n### \"Survey shows to fewer users than expected\"\n\n- **`surveyPopupDelaySeconds` + URL re-check (web only).** After the event fires and eligibility passes, the SDK waits N seconds, then re-checks `doesSurveyUrlMatch` against the _current_ URL before rendering (`handlePopoverSurvey`). If the user navigated during the delay, the survey is silently dropped — no `survey shown`. Common on navigation-heavy apps with a non-trivial delay. Fix: lower the delay to 0–2s.\n- **`seenSurveyWaitPeriodInDays` + the customer's other surveys.** Any survey shown to a user updates `lastSeenSurveyDate`; this survey is then blocked for the wait window. Completion status is irrelevant. Verify by checking whether the _unshown_ cohort saw another survey recently — and confirm against a control group (do the _shown_ users differ?). Fix: lower the wait period, or pause competing surveys.\n- **Cohort composition changed.** If the survey targets a cohort and someone edited the source dynamic cohort (e.g. added a behavioral filter), every static snapshot taken afterward inherits the narrower definition. Reach drops without any survey-side change. Find it in the flag activity log (cohort swap) and confirm cohort sizes via `static_cohort_people`.\n\n### \"Event-based survey never fires\"\n\n- **Timing race at session start.** Event captured before `\u002Fapi\u002Fsurveys` returns and the capture hook registers. Signature: event fires very early in session. Unavoidable client-side; mitigate by triggering on a slightly later event.\n- **Group-aggregated `linked_flag` with no group context.** If `linked_flag` (or targeting flag) has `aggregation_group_type_index` set, it evaluates against a _group_, not the person. Without `posthog.group(\u003Cindex>, \u003Ckey>)` set before the event fires, the flag returns **false** and the survey never shows. Signature: `$feature_flag_called` returns `false` with empty `$groups`, and the `$feature\u002F\u003Ckey>` property is missing from the trigger events. Fix: set group context in the SDK, or switch the survey to a person-level flag.\n- **Customer wired the survey to the wrong flag.** They create a flag with email\u002Fproperty targeting but the survey's `linked_flag`\u002F`targeting_flag` points at a _different_ flag. Always confirm the actual `linked_flag.key` \u002F `targeting_flag.key` from the API — don't trust the customer's description.\n- **Behavioral cohort in a realtime flag.** A cohort with `performed_event`\u002Fbehavioral filters can't be evaluated in realtime flag bytecode (`\"Unsupported behavioral filter for realtime bytecode\"`, `posthog\u002Fapi\u002Fcohort.py`). The cohort shows a `bytecode_error`. Surveys\u002Fflags can't use it directly — the customer must make a _static_ copy of the cohort and target that.\n\n### \"Responses show as zero in the UI but raw events exist\"\n\n- **Max AI corrupted the survey definition.** Max's `edit_survey` tool (`products\u002Fsurveys\u002Fbackend\u002Fmax_tools.py`) has two failure modes: (a) on reorder\u002Fedit it rebuilds each question from `QUESTION_TYPE_MAP` (`nps`→scale 10, `csat`→scale 5, etc.), so picking the wrong semantic type silently changes a question's scale; (b) the `id` field expects 1-indexed labels (`\"1\"`,`\"2\"`) — passing a real UUID falls through and a _fresh_ UUID is generated, so responses keyed by `$survey_response_\u003Cold_uuid>` no longer join to the question. Raw events are intact; only the definition is wrong. Fix: PATCH the `questions` array back to the original UUIDs (recoverable from the response events) and restore the question type. Tell the customer to edit question _text_ via the UI, and avoid asking Max to reorder questions on a survey with historical responses until the tool guards UUIDs.\n\n### \"Cohort count shows 0 but the cohort is populated\"\n\n- Cosmetic UI bug, does **not** affect targeting. Confirm the real count via `static_cohort_people`. NOTE: this is _not_ a simple one-line bug — the normal `insert_cohort_from_query` path does recompute count via `count_cohort_members`; the `count=0` display only appears on certain failure paths. Do not promise a quick fix without reproducing the specific path.\n\n### General caution\n\n- **Aged tickets are dirty.** Config may have been edited by the customer or a prior agent during troubleshooting. Pull activity logs; frame secondary findings as \"while you're in there, double-check X\" rather than \"we found X is broken.\"\n- **The stats UI can undercount vs raw `survey shown` events.** If the numbers don't reconcile, trust the raw events and flag the discrepancy as a separate follow-up.\n\n## Diagnostic queries\n\nRead-only HogQL templates for the disambiguators and confirmations above live in\n[references\u002Fdiagnostic-queries.md](references\u002Fdiagnostic-queries.md). Run them via the\nPostHog MCP `execute-sql` against the customer's project.\n\n## Access for debugging\n\nOnly investigate a project tied to a genuine support request from that customer — the IDs\nshould come from a real ticket, not from someone asking you to look up an org\u002Fsurvey they\ncan't point to a request for. Staff access is broad; don't freelance across projects.\n\nPrefer **read-only** paths in this order:\n\n1. **PostHog MCP tools** (`survey`, `feature-flag`, `cohorts`, `execute-sql`, `activity-log`, `persons`) against the customer's project. This is read-only by default and the safest way to inspect config and run queries — no impersonation, no write risk. Use this first.\n2. **Survey\u002Fflag API endpoints** read via the browser while impersonating (staff). Good for the full JSON the MCP may not surface verbatim (e.g. raw `internal_targeting_flag.filters`).\n3. **Django admin** only when 1 and 2 can't answer it. It's powerful and write-capable, so treat it as read-only by discipline: look, don't change. Never edit a customer's survey\u002Fflag\u002Fcohort from admin without explicit customer consent.\n\nWhen you need a value the MCP can't infer (project ID, instance, which survey), ask the\noperator to paste the survey API JSON — it skips several round-trips.\n\n## Writing the customer reply\n\nVoice derived from the PostHog handbook support values (reassuringly human, humble, ship\nfixes, clear with no jargon). Rules:\n\n- **Lead with the cause, then the fix.** One line on what's happening, then what to do.\n- **Bold the issue and bold each action.** Make the problem and the next step scannable.\n- **Use the labels the customer sees in the UI,** never internal field names. Grep `frontend\u002Fsrc\u002Fscenes\u002Fsurveys\u002F` for the real string. E.g. `surveyPopupDelaySeconds` → \"Delay survey popup by at least N seconds once the display conditions are met\"; the wait period is \"Survey wait period\" \u002F \"Don't show this survey if another one was shown to the user in the last N days\". The customer's own event names stay verbatim.\n- **Link every PostHog entity** by ID. Cohorts: `https:\u002F\u002F\u003Cus|eu>.posthog.com\u002Fproject\u002F\u003Cid>\u002Fcohorts\u002F\u003Ccohort_id>`. Flags: `...\u002Ffeature_flags\u002F\u003Cflag_id>`. Surveys: `...\u002Fsurveys\u002F\u003Csurvey_id>`. Match the customer's instance (US vs EU).\n- **Predict the expected outcome** so the customer can verify the fix worked (\"you should see ~X going forward\").\n- **Gauge the customer's technical level.** If they can run SQL and hit the API, offer the patch path. If not, offer to apply the fix ourselves (and confirm any destructive detail first).\n- **Do NOT offer to \"hop on a call\" or book a meeting.** PostHog support is async-first. Close with \"We're always here if you need a follow-up.\"\n- **Never leak internals** — no MCP tool names, code paths, line numbers, Django admin, staff impersonation, or other customers. Keep it to product concepts a customer recognizes.\n- **Run the final draft through a humanizer skill before sending** (if you have one, e.g. `humanizer`). Strip em dashes, setup phrases (\"Here's the thing:\", \"Three things to know\"), rule-of-three padding, and tidy parallel list structure. The reply should read like a person typed it.\n\nReply skeleton:\n\n```text\nHi \u003Cname>,\n\n\u003Cone line: tracked it down + the cause in plain terms.>\n\n**The problem:** \u003Cwhat's happening, with the evidence you pulled.>\n\n**\u003CFastest fix \u002F two ways to fix it>:**\n1. **\u003Caction>** — \u003Cwhy \u002F how.>\n2. **\u003Caction>** — \u003Cwhy \u002F how.>\n\n**Also worth knowing while you're in there:** \u003Csecondary finding, softened.>\n\nWe're always here if you need a follow-up.\n```\n\n## Feature work — shipping across SDKs\n\nA survey capability is only \"done\" when it works (or is deliberately scoped out) on every\nSDK a customer might use. When building or changing survey behavior:\n\n1. Land the backend\u002FUI change in this repo (serializer + `frontend\u002Fsrc\u002Fscenes\u002Fsurveys\u002F`).\n2. Decide the per-SDK story using the parity table. If a feature lands web-only (like\n   `surveyPopupDelaySeconds`), say so explicitly in the docs and the PR — silent gaps\n   become support tickets.\n3. Implement in the SDK repos (`posthog-js` covers both web and React Native), then\n   `posthog-ios`, `posthog-android`, and the Flutter Dart layer. Remember Flutter's split:\n   eligibility\u002Ftrigger logic is native (iOS\u002FAndroid), rendering is Dart. Use the registry\n   in [references\u002Flocal-repos.md](references\u002Flocal-repos.md) to find each checkout.\n4. Update the `posthog.com` docs (`contents\u002Fdocs\u002Fsurveys\u002F`) and this parity table.\n5. Use the `survey-sdk-audit` skill (if available) to confirm version requirements and cross-SDK coverage.\n",{"data":49,"body":50},{"name":4,"description":6},{"type":51,"children":52},"root",[53,61,90,97,135,351,356,362,375,679,684,775,788,794,814,926,939,944,1084,1090,1309,1315,1320,1327,1428,1434,1633,1639,1744,1750,1801,1807,1837,1843,1862,1868,1873,1885,1966,1971,1977,1982,2121,2126,2138,2144,2149],{"type":54,"tag":55,"props":56,"children":57},"element","h1",{"id":4},[58],{"type":59,"value":60},"text","Debugging surveys",{"type":54,"tag":62,"props":63,"children":64},"p",{},[65,67,73,75,81,83,88],{"type":59,"value":66},"PostHog Surveys is a no-code in-app form builder. A customer creates a survey in the\nPostHog UI; it must then be evaluated and rendered by whichever SDK their app runs.\n",{"type":54,"tag":68,"props":69,"children":70},"strong",{},[71],{"type":59,"value":72},"Most \"survey not showing\" tickets are eligibility problems, not rendering bugs",{"type":59,"value":74}," — the\nSDK correctly decided the user is not eligible, and the job is to find ",{"type":54,"tag":76,"props":77,"children":78},"em",{},[79],{"type":59,"value":80},"which",{"type":59,"value":82}," gate\nfailed and ",{"type":54,"tag":76,"props":84,"children":85},{},[86],{"type":59,"value":87},"why",{"type":59,"value":89},".",{"type":54,"tag":91,"props":92,"children":94},"h2",{"id":93},"repos",[95],{"type":59,"value":96},"Repos",{"type":54,"tag":62,"props":98,"children":99},{},[100,102,108,110,117,119,125,127,133],{"type":59,"value":101},"GitHub is the source of truth for where the code lives. When you need to read or change SDK\nsource, resolve a local checkout via the registry described in\n",{"type":54,"tag":103,"props":104,"children":106},"a",{"href":105},"references\u002Flocal-repos.md",[107],{"type":59,"value":105},{"type":59,"value":109}," so a clone is found once and reused —\ndon't re-clone every session. First time on a machine, run ",{"type":54,"tag":111,"props":112,"children":114},"code",{"className":113},[],[115],{"type":59,"value":116},"python3 scripts\u002Frepos.py init",{"type":59,"value":118},"\nto auto-discover existing checkouts; thereafter ",{"type":54,"tag":111,"props":120,"children":122},{"className":121},[],[123],{"type":59,"value":124},"python3 scripts\u002Frepos.py ensure \u003Crepo>",{"type":59,"value":126},"\nprints the path (and ",{"type":54,"tag":111,"props":128,"children":130},{"className":129},[],[131],{"type":59,"value":132},"--clone",{"type":59,"value":134}," clones if missing).",{"type":54,"tag":136,"props":137,"children":138},"table",{},[139,163],{"type":54,"tag":140,"props":141,"children":142},"thead",{},[143],{"type":54,"tag":144,"props":145,"children":146},"tr",{},[147,153,158],{"type":54,"tag":148,"props":149,"children":150},"th",{},[151],{"type":59,"value":152},"Concern",{"type":54,"tag":148,"props":154,"children":155},{},[156],{"type":59,"value":157},"Repo",{"type":54,"tag":148,"props":159,"children":160},{},[161],{"type":59,"value":162},"Where to look",{"type":54,"tag":164,"props":165,"children":166},"tbody",{},[167,200,228,255,278,301,324],{"type":54,"tag":144,"props":168,"children":169},{},[170,176,181],{"type":54,"tag":171,"props":172,"children":173},"td",{},[174],{"type":59,"value":175},"Product UI + backend",{"type":54,"tag":171,"props":177,"children":178},{},[179],{"type":59,"value":180},"this monorepo (PostHog\u002Fposthog)",{"type":54,"tag":171,"props":182,"children":183},{},[184,186,192,194],{"type":59,"value":185},"UI: ",{"type":54,"tag":111,"props":187,"children":189},{"className":188},[],[190],{"type":59,"value":191},"frontend\u002Fsrc\u002Fscenes\u002Fsurveys\u002F",{"type":59,"value":193},", backend: ",{"type":54,"tag":111,"props":195,"children":197},{"className":196},[],[198],{"type":59,"value":199},"products\u002Fsurveys\u002Fbackend\u002F",{"type":54,"tag":144,"props":201,"children":202},{},[203,208,219],{"type":54,"tag":171,"props":204,"children":205},{},[206],{"type":59,"value":207},"Web SDK",{"type":54,"tag":171,"props":209,"children":210},{},[211],{"type":54,"tag":103,"props":212,"children":216},{"href":213,"rel":214},"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-js",[215],"nofollow",[217],{"type":59,"value":218},"PostHog\u002Fposthog-js",{"type":54,"tag":171,"props":220,"children":221},{},[222],{"type":54,"tag":111,"props":223,"children":225},{"className":224},[],[226],{"type":59,"value":227},"packages\u002Fbrowser\u002F",{"type":54,"tag":144,"props":229,"children":230},{},[231,236,246],{"type":54,"tag":171,"props":232,"children":233},{},[234],{"type":59,"value":235},"React Native SDK",{"type":54,"tag":171,"props":237,"children":238},{},[239,244],{"type":54,"tag":103,"props":240,"children":242},{"href":213,"rel":241},[215],[243],{"type":59,"value":218},{"type":59,"value":245}," (same monorepo)",{"type":54,"tag":171,"props":247,"children":248},{},[249],{"type":54,"tag":111,"props":250,"children":252},{"className":251},[],[253],{"type":59,"value":254},"packages\u002Freact-native\u002F",{"type":54,"tag":144,"props":256,"children":257},{},[258,263,273],{"type":54,"tag":171,"props":259,"children":260},{},[261],{"type":59,"value":262},"iOS SDK",{"type":54,"tag":171,"props":264,"children":265},{},[266],{"type":54,"tag":103,"props":267,"children":270},{"href":268,"rel":269},"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-ios",[215],[271],{"type":59,"value":272},"PostHog\u002Fposthog-ios",{"type":54,"tag":171,"props":274,"children":275},{},[276],{"type":59,"value":277},"survey rendering + eligibility",{"type":54,"tag":144,"props":279,"children":280},{},[281,286,296],{"type":54,"tag":171,"props":282,"children":283},{},[284],{"type":59,"value":285},"Android SDK",{"type":54,"tag":171,"props":287,"children":288},{},[289],{"type":54,"tag":103,"props":290,"children":293},{"href":291,"rel":292},"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-android",[215],[294],{"type":59,"value":295},"PostHog\u002Fposthog-android",{"type":54,"tag":171,"props":297,"children":298},{},[299],{"type":59,"value":300},"eligibility (delegate-based UI)",{"type":54,"tag":144,"props":302,"children":303},{},[304,309,319],{"type":54,"tag":171,"props":305,"children":306},{},[307],{"type":59,"value":308},"Flutter SDK",{"type":54,"tag":171,"props":310,"children":311},{},[312],{"type":54,"tag":103,"props":313,"children":316},{"href":314,"rel":315},"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog-flutter",[215],[317],{"type":59,"value":318},"PostHog\u002Fposthog-flutter",{"type":54,"tag":171,"props":320,"children":321},{},[322],{"type":59,"value":323},"Dart rendering; native iOS\u002FAndroid handles eligibility",{"type":54,"tag":144,"props":325,"children":326},{},[327,332,342],{"type":54,"tag":171,"props":328,"children":329},{},[330],{"type":59,"value":331},"Public docs",{"type":54,"tag":171,"props":333,"children":334},{},[335],{"type":54,"tag":103,"props":336,"children":339},{"href":337,"rel":338},"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog.com",[215],[340],{"type":59,"value":341},"PostHog\u002Fposthog.com",{"type":54,"tag":171,"props":343,"children":344},{},[345],{"type":54,"tag":111,"props":346,"children":348},{"className":347},[],[349],{"type":59,"value":350},"contents\u002Fdocs\u002Fsurveys\u002F",{"type":54,"tag":62,"props":352,"children":353},{},[354],{"type":59,"value":355},"Always check the local checkout is present and on a sane branch before quoting code; line\nnumbers drift, so grep for the symbol rather than trusting a remembered line number.",{"type":54,"tag":91,"props":357,"children":359},{"id":358},"cross-sdk-feature-parity-check-this-first",[360],{"type":59,"value":361},"Cross-SDK feature parity (check this FIRST)",{"type":54,"tag":62,"props":363,"children":364},{},[365,367,373],{"type":59,"value":366},"A large class of tickets is \"customer expects a feature their platform doesn't support.\"\nConfirm the survey's ",{"type":54,"tag":111,"props":368,"children":370},{"className":369},[],[371],{"type":59,"value":372},"lib",{"type":59,"value":374}," \u002F the customer's platform before anything else, then consult\nthis table. Verified against the SDK source — re-verify if it's been months, the gaps\nget filled over time.",{"type":54,"tag":136,"props":376,"children":377},{},[378,414],{"type":54,"tag":140,"props":379,"children":380},{},[381],{"type":54,"tag":144,"props":382,"children":383},{},[384,389,394,399,404,409],{"type":54,"tag":148,"props":385,"children":386},{},[387],{"type":59,"value":388},"Feature",{"type":54,"tag":148,"props":390,"children":391},{},[392],{"type":59,"value":393},"Web (posthog-js)",{"type":54,"tag":148,"props":395,"children":396},{},[397],{"type":59,"value":398},"iOS",{"type":54,"tag":148,"props":400,"children":401},{},[402],{"type":59,"value":403},"Android",{"type":54,"tag":148,"props":405,"children":406},{},[407],{"type":59,"value":408},"Flutter",{"type":54,"tag":148,"props":410,"children":411},{},[412],{"type":59,"value":413},"React Native",{"type":54,"tag":164,"props":415,"children":416},{},[417,477,508,566,594,632],{"type":54,"tag":144,"props":418,"children":419},{},[420,425,430,443,453,465],{"type":54,"tag":171,"props":421,"children":422},{},[423],{"type":59,"value":424},"Rendering",{"type":54,"tag":171,"props":426,"children":427},{},[428],{"type":59,"value":429},"DOM + shadow root",{"type":54,"tag":171,"props":431,"children":432},{},[433,435,441],{"type":59,"value":434},"Native SwiftUI (",{"type":54,"tag":111,"props":436,"children":438},{"className":437},[],[439],{"type":59,"value":440},"SurveysWindow",{"type":59,"value":442},")",{"type":54,"tag":171,"props":444,"children":445},{},[446,451],{"type":54,"tag":68,"props":447,"children":448},{},[449],{"type":59,"value":450},"No built-in UI",{"type":59,"value":452}," — delegate only",{"type":54,"tag":171,"props":454,"children":455},{},[456,458,464],{"type":59,"value":457},"Dart widgets (",{"type":54,"tag":111,"props":459,"children":461},{"className":460},[],[462],{"type":59,"value":463},"SurveyBottomSheet",{"type":59,"value":442},{"type":54,"tag":171,"props":466,"children":467},{},[468,470,476],{"type":59,"value":469},"RN components (",{"type":54,"tag":111,"props":471,"children":473},{"className":472},[],[474],{"type":59,"value":475},"SurveyModal",{"type":59,"value":442},{"type":54,"tag":144,"props":478,"children":479},{},[480,485,490,495,499,504],{"type":54,"tag":171,"props":481,"children":482},{},[483],{"type":59,"value":484},"Event-based triggers",{"type":54,"tag":171,"props":486,"children":487},{},[488],{"type":59,"value":489},"yes (since 1.137.0, 2024-06-05)",{"type":54,"tag":171,"props":491,"children":492},{},[493],{"type":59,"value":494},"yes",{"type":54,"tag":171,"props":496,"children":497},{},[498],{"type":59,"value":494},{"type":54,"tag":171,"props":500,"children":501},{},[502],{"type":59,"value":503},"yes (native side)",{"type":54,"tag":171,"props":505,"children":506},{},[507],{"type":59,"value":494},{"type":54,"tag":144,"props":509,"children":510},{},[511,516,520,539,547,556],{"type":54,"tag":171,"props":512,"children":513},{},[514],{"type":59,"value":515},"URL \u002F screen targeting",{"type":54,"tag":171,"props":517,"children":518},{},[519],{"type":59,"value":494},{"type":54,"tag":171,"props":521,"children":522},{},[523,525,530,532,538],{"type":59,"value":524},"decoded but ",{"type":54,"tag":68,"props":526,"children":527},{},[528],{"type":59,"value":529},"NOT evaluated",{"type":59,"value":531}," (",{"type":54,"tag":111,"props":533,"children":535},{"className":534},[],[536],{"type":59,"value":537},"\u002F\u002F TODO",{"type":59,"value":442},{"type":54,"tag":171,"props":540,"children":541},{},[542,543],{"type":59,"value":524},{"type":54,"tag":68,"props":544,"children":545},{},[546],{"type":59,"value":529},{"type":54,"tag":171,"props":548,"children":549},{},[550,554],{"type":54,"tag":68,"props":551,"children":552},{},[553],{"type":59,"value":529},{"type":59,"value":555}," (native gap)",{"type":54,"tag":171,"props":557,"children":558},{},[559,564],{"type":54,"tag":68,"props":560,"children":561},{},[562],{"type":59,"value":563},"explicitly excluded",{"type":59,"value":565}," in filter",{"type":54,"tag":144,"props":567,"children":568},{},[569,574,578,582,586,590],{"type":54,"tag":171,"props":570,"children":571},{},[572],{"type":59,"value":573},"Feature-flag \u002F cohort targeting",{"type":54,"tag":171,"props":575,"children":576},{},[577],{"type":59,"value":494},{"type":54,"tag":171,"props":579,"children":580},{},[581],{"type":59,"value":494},{"type":54,"tag":171,"props":583,"children":584},{},[585],{"type":59,"value":494},{"type":54,"tag":171,"props":587,"children":588},{},[589],{"type":59,"value":503},{"type":54,"tag":171,"props":591,"children":592},{},[593],{"type":59,"value":494},{"type":54,"tag":144,"props":595,"children":596},{},[597,606,610,614,618,622],{"type":54,"tag":171,"props":598,"children":599},{},[600],{"type":54,"tag":111,"props":601,"children":603},{"className":602},[],[604],{"type":59,"value":605},"seenSurveyWaitPeriodInDays",{"type":54,"tag":171,"props":607,"children":608},{},[609],{"type":59,"value":494},{"type":54,"tag":171,"props":611,"children":612},{},[613],{"type":59,"value":494},{"type":54,"tag":171,"props":615,"children":616},{},[617],{"type":59,"value":494},{"type":54,"tag":171,"props":619,"children":620},{},[621],{"type":59,"value":503},{"type":54,"tag":171,"props":623,"children":624},{},[625,627],{"type":59,"value":626},"stored but ",{"type":54,"tag":68,"props":628,"children":629},{},[630],{"type":59,"value":631},"comparison commented out",{"type":54,"tag":144,"props":633,"children":634},{},[635,644,648,656,663,670],{"type":54,"tag":171,"props":636,"children":637},{},[638],{"type":54,"tag":111,"props":639,"children":641},{"className":640},[],[642],{"type":59,"value":643},"surveyPopupDelaySeconds",{"type":54,"tag":171,"props":645,"children":646},{},[647],{"type":59,"value":494},{"type":54,"tag":171,"props":649,"children":650},{},[651],{"type":54,"tag":68,"props":652,"children":653},{},[654],{"type":59,"value":655},"not implemented",{"type":54,"tag":171,"props":657,"children":658},{},[659],{"type":54,"tag":68,"props":660,"children":661},{},[662],{"type":59,"value":655},{"type":54,"tag":171,"props":664,"children":665},{},[666],{"type":54,"tag":68,"props":667,"children":668},{},[669],{"type":59,"value":655},{"type":54,"tag":171,"props":671,"children":672},{},[673,677],{"type":54,"tag":68,"props":674,"children":675},{},[676],{"type":59,"value":655},{"type":59,"value":678}," (TODO)",{"type":54,"tag":62,"props":680,"children":681},{},[682],{"type":59,"value":683},"Consequences worth memorizing:",{"type":54,"tag":685,"props":686,"children":687},"ul",{},[688,704,714,732,765],{"type":54,"tag":689,"props":690,"children":691},"li",{},[692,702],{"type":54,"tag":68,"props":693,"children":694},{},[695,700],{"type":54,"tag":111,"props":696,"children":698},{"className":697},[],[699],{"type":59,"value":643},{"type":59,"value":701}," is web-only.",{"type":59,"value":703}," If a mobile ticket blames the delay, it's a red herring.",{"type":54,"tag":689,"props":705,"children":706},{},[707,712],{"type":54,"tag":68,"props":708,"children":709},{},[710],{"type":59,"value":711},"URL targeting is effectively web-only.",{"type":59,"value":713}," Mobile SDKs decode the field but never enforce it; React Native filters those surveys out entirely. A mobile survey with a URL condition behaves as \"no URL condition\" (mobile\u002Fflutter) or \"never shows\" (RN).",{"type":54,"tag":689,"props":715,"children":716},{},[717,722,724,730],{"type":54,"tag":68,"props":718,"children":719},{},[720],{"type":59,"value":721},"Android ships no survey UI.",{"type":59,"value":723}," The app (or the Flutter plugin) must provide a ",{"type":54,"tag":111,"props":725,"children":727},{"className":726},[],[728],{"type":59,"value":729},"PostHogSurveysDelegate",{"type":59,"value":731},". \"Survey never renders on Android\" is often a missing delegate, not a PostHog bug.",{"type":54,"tag":689,"props":733,"children":734},{},[735,740,742,748,750,756,758,763],{"type":54,"tag":68,"props":736,"children":737},{},[738],{"type":59,"value":739},"Flutter is hybrid:",{"type":59,"value":741}," triggering\u002Feligibility runs in the native iOS\u002FAndroid layer; rendering is Dart (",{"type":54,"tag":111,"props":743,"children":745},{"className":744},[],[746],{"type":59,"value":747},"SurveyService.showSurvey",{"type":59,"value":749}," → ",{"type":54,"tag":111,"props":751,"children":753},{"className":752},[],[754],{"type":59,"value":755},"showModalBottomSheet",{"type":59,"value":757},"). It does ",{"type":54,"tag":76,"props":759,"children":760},{},[761],{"type":59,"value":762},"not",{"type":59,"value":764}," \"just call native\" for UI. So a Flutter rendering bug lives in Dart; a Flutter eligibility bug lives in native.",{"type":54,"tag":689,"props":766,"children":767},{},[768,773],{"type":54,"tag":68,"props":769,"children":770},{},[771],{"type":59,"value":772},"React Native wait period is silently disabled",{"type":59,"value":774}," (the check is commented out). Don't blame the wait period on RN.",{"type":54,"tag":62,"props":776,"children":777},{},[778,780,786],{"type":59,"value":779},"For a deeper version-by-version capability audit, see the ",{"type":54,"tag":111,"props":781,"children":783},{"className":782},[],[784],{"type":59,"value":785},"survey-sdk-audit",{"type":59,"value":787}," skill if available.",{"type":54,"tag":91,"props":789,"children":791},{"id":790},"how-a-survey-actually-gets-shown-the-web-eligibility-pipeline",[792],{"type":59,"value":793},"How a survey actually gets shown (the web eligibility pipeline)",{"type":54,"tag":62,"props":795,"children":796},{},[797,799,805,806,812],{"type":59,"value":798},"The web SDK is the most complex and the most common in tickets. Mental model from\n",{"type":54,"tag":111,"props":800,"children":802},{"className":801},[],[803],{"type":59,"value":804},"packages\u002Fbrowser\u002Fsrc\u002Fextensions\u002Fsurveys.tsx",{"type":59,"value":531},{"type":54,"tag":111,"props":807,"children":809},{"className":808},[],[810],{"type":59,"value":811},"checkSurveyEligibility",{"type":59,"value":813},") — checks run in\norder, first failure wins:",{"type":54,"tag":815,"props":816,"children":817},"ol",{},[818,844,857,868,879,890,915],{"type":54,"tag":689,"props":819,"children":820},{},[821,827,829,835,837,843],{"type":54,"tag":111,"props":822,"children":824},{"className":823},[],[825],{"type":59,"value":826},"isSurveyRunning",{"type":59,"value":828}," — has ",{"type":54,"tag":111,"props":830,"children":832},{"className":831},[],[833],{"type":59,"value":834},"start_date",{"type":59,"value":836},", no ",{"type":54,"tag":111,"props":838,"children":840},{"className":839},[],[841],{"type":59,"value":842},"end_date",{"type":59,"value":89},{"type":54,"tag":689,"props":845,"children":846},{},[847,849,855],{"type":59,"value":848},"survey ",{"type":54,"tag":111,"props":850,"children":852},{"className":851},[],[853],{"type":59,"value":854},"type",{"type":59,"value":856}," is in-app (Popover \u002F Widget \u002F API).",{"type":54,"tag":689,"props":858,"children":859},{},[860,866],{"type":54,"tag":111,"props":861,"children":863},{"className":862},[],[864],{"type":59,"value":865},"linked_flag_key",{"type":59,"value":867}," enabled (if set).",{"type":54,"tag":689,"props":869,"children":870},{},[871,877],{"type":54,"tag":111,"props":872,"children":874},{"className":873},[],[875],{"type":59,"value":876},"targeting_flag_key",{"type":59,"value":878}," enabled (if set) — customer-defined property targeting.",{"type":54,"tag":689,"props":880,"children":881},{},[882,888],{"type":54,"tag":111,"props":883,"children":885},{"className":884},[],[886],{"type":59,"value":887},"_internalFlagCheckSatisfied",{"type":59,"value":889}," — the auto-generated internal targeting flag.",{"type":54,"tag":689,"props":891,"children":892},{},[893,899,901,906,908,914],{"type":54,"tag":111,"props":894,"children":896},{"className":895},[],[897],{"type":59,"value":898},"hasWaitPeriodPassed",{"type":59,"value":900}," — ",{"type":54,"tag":111,"props":902,"children":904},{"className":903},[],[905],{"type":59,"value":605},{"type":59,"value":907}," vs ",{"type":54,"tag":111,"props":909,"children":911},{"className":910},[],[912],{"type":59,"value":913},"localStorage.lastSeenSurveyDate",{"type":59,"value":89},{"type":54,"tag":689,"props":916,"children":917},{},[918,924],{"type":54,"tag":111,"props":919,"children":921},{"className":920},[],[922],{"type":59,"value":923},"getSurveySeen",{"type":59,"value":925}," — per-survey seen flag.",{"type":54,"tag":62,"props":927,"children":928},{},[929,931,937],{"type":59,"value":930},"Then in ",{"type":54,"tag":111,"props":932,"children":934},{"className":933},[],[935],{"type":59,"value":936},"getActiveMatchingSurveys",{"type":59,"value":938},": URL\u002Fdevice\u002Fselector match, event\u002Faction trigger fired, and flag re-check.",{"type":54,"tag":62,"props":940,"children":941},{},[942],{"type":59,"value":943},"Two non-obvious facts that drive real tickets:",{"type":54,"tag":685,"props":945,"children":946},{},[947,977],{"type":54,"tag":689,"props":948,"children":949},{},[950,955,956,962,964,970,971,975],{"type":54,"tag":68,"props":951,"children":952},{},[953],{"type":59,"value":954},"The server returns ALL non-archived surveys",{"type":59,"value":531},{"type":54,"tag":111,"props":957,"children":959},{"className":958},[],[960],{"type":59,"value":961},"SurveyViewSet",{"type":59,"value":963},", ",{"type":54,"tag":111,"props":965,"children":967},{"className":966},[],[968],{"type":59,"value":969},"products\u002Fsurveys\u002Fbackend\u002Fapi\u002Fsurvey.py",{"type":59,"value":757},{"type":54,"tag":68,"props":972,"children":973},{},[974],{"type":59,"value":762},{"type":59,"value":976}," pre-filter by the internal targeting flag. All eligibility is client-side. So you cannot conclude \"the backend excluded them\" — the SDK did.",{"type":54,"tag":689,"props":978,"children":979},{},[980,985,987,993,995,1001,1003,1008,1010,1016,1018,1024,1026,1031,1033,1038,1040,1045,1047,1052,1054,1059,1061,1067,1069,1075,1077,1082],{"type":54,"tag":68,"props":981,"children":982},{},[983],{"type":59,"value":984},"The wait period has TWO independent implementations.",{"type":59,"value":986}," ",{"type":54,"tag":111,"props":988,"children":990},{"className":989},[],[991],{"type":59,"value":992},"canActivateRepeatedly",{"type":59,"value":994}," (true when ",{"type":54,"tag":111,"props":996,"children":998},{"className":997},[],[999],{"type":59,"value":1000},"schedule: 'always'",{"type":59,"value":1002},") short-circuits ",{"type":54,"tag":111,"props":1004,"children":1006},{"className":1005},[],[1007],{"type":59,"value":887},{"type":59,"value":1009}," (step 5) — so ",{"type":54,"tag":111,"props":1011,"children":1013},{"className":1012},[],[1014],{"type":59,"value":1015},"always",{"type":59,"value":1017}," bypasses the internal flag, including its ",{"type":54,"tag":111,"props":1019,"children":1021},{"className":1020},[],[1022],{"type":59,"value":1023},"$last_seen_survey_date",{"type":59,"value":1025}," rule. But ",{"type":54,"tag":111,"props":1027,"children":1029},{"className":1028},[],[1030],{"type":59,"value":898},{"type":59,"value":1032}," (step 6) reads ",{"type":54,"tag":111,"props":1034,"children":1036},{"className":1035},[],[1037],{"type":59,"value":913},{"type":59,"value":1039}," directly and is ",{"type":54,"tag":68,"props":1041,"children":1042},{},[1043],{"type":59,"value":1044},"NOT",{"type":59,"value":1046}," bypassed by ",{"type":54,"tag":111,"props":1048,"children":1050},{"className":1049},[],[1051],{"type":59,"value":992},{"type":59,"value":1053},". So a ",{"type":54,"tag":111,"props":1055,"children":1057},{"className":1056},[],[1058],{"type":59,"value":1000},{"type":59,"value":1060}," survey with ",{"type":54,"tag":111,"props":1062,"children":1064},{"className":1063},[],[1065],{"type":59,"value":1066},"seenSurveyWaitPeriodInDays: 30",{"type":59,"value":1068}," still enforces the 30-day wait via the localStorage path. ",{"type":54,"tag":111,"props":1070,"children":1072},{"className":1071},[],[1073],{"type":59,"value":1074},"lastSeenSurveyDate",{"type":59,"value":1076}," is updated whenever ",{"type":54,"tag":76,"props":1078,"children":1079},{},[1080],{"type":59,"value":1081},"any",{"type":59,"value":1083}," survey is shown, regardless of completion.",{"type":54,"tag":91,"props":1085,"children":1087},{"id":1086},"debugging-workflow",[1088],{"type":59,"value":1089},"Debugging workflow",{"type":54,"tag":815,"props":1091,"children":1092},{},[1093,1110,1149,1166,1240,1266,1299],{"type":54,"tag":689,"props":1094,"children":1095},{},[1096,1101,1103,1108],{"type":54,"tag":68,"props":1097,"children":1098},{},[1099],{"type":59,"value":1100},"Parse the ticket.",{"type":59,"value":1102}," Extract: org\u002Fproject ID, instance (US vs EU — URLs differ), survey ID(s), the ",{"type":54,"tag":111,"props":1104,"children":1106},{"className":1105},[],[1107],{"type":59,"value":372},{"type":59,"value":1109}," (platform), the symptom in precise terms, and what the customer already tried. If the ticket is aged or has prior support replies, the config may have been edited mid-thread — treat earlier claims as stale and re-pull current state.",{"type":54,"tag":689,"props":1111,"children":1112},{},[1113,1118,1120,1126,1127,1133,1135,1140,1142,1147],{"type":54,"tag":68,"props":1114,"children":1115},{},[1116],{"type":59,"value":1117},"Disambiguate \"none\" vs \"fewer.\"",{"type":59,"value":1119}," Customers say \"no responses\" when they mean \"fewer.\" Pull the ",{"type":54,"tag":111,"props":1121,"children":1123},{"className":1122},[],[1124],{"type":59,"value":1125},"survey shown",{"type":59,"value":907},{"type":54,"tag":111,"props":1128,"children":1130},{"className":1129},[],[1131],{"type":59,"value":1132},"survey sent",{"type":59,"value":1134}," counts before\u002Fafter the suspected change (see ",{"type":54,"tag":103,"props":1136,"children":1138},{"href":1137},"references\u002Fdiagnostic-queries.md",[1139],{"type":59,"value":1137},{"type":59,"value":1141},"). If the ",{"type":54,"tag":76,"props":1143,"children":1144},{},[1145],{"type":59,"value":1146},"response rate",{"type":59,"value":1148}," (sent\u002Fshown) is stable, the problem is upstream eligibility (fewer people shown), not rendering or submission. This single check redirects most investigations correctly.",{"type":54,"tag":689,"props":1150,"children":1151},{},[1152,1157,1159,1164],{"type":54,"tag":68,"props":1153,"children":1154},{},[1155],{"type":59,"value":1156},"Platform parity check.",{"type":59,"value":1158}," Confirm the ",{"type":54,"tag":111,"props":1160,"children":1162},{"className":1161},[],[1163],{"type":59,"value":372},{"type":59,"value":1165}," and consult the parity table. Eliminate features the platform doesn't support before investigating them.",{"type":54,"tag":689,"props":1167,"children":1168},{},[1169,1174,1175,1181,1183,1189,1191,1197,1198,1204,1205,1211,1212,1218,1219,1225,1226,1232,1233,1239],{"type":54,"tag":68,"props":1170,"children":1171},{},[1172],{"type":59,"value":1173},"Pull the survey definition.",{"type":59,"value":986},{"type":54,"tag":111,"props":1176,"children":1178},{"className":1177},[],[1179],{"type":59,"value":1180},"GET \u002Fapi\u002Fprojects\u002F\u003Cid>\u002Fsurveys\u002F\u003Csid>\u002F",{"type":59,"value":1182},". Inspect ",{"type":54,"tag":111,"props":1184,"children":1186},{"className":1185},[],[1187],{"type":59,"value":1188},"conditions",{"type":59,"value":1190}," (events, url, seenSurveyWaitPeriodInDays), ",{"type":54,"tag":111,"props":1192,"children":1194},{"className":1193},[],[1195],{"type":59,"value":1196},"appearance.surveyPopupDelaySeconds",{"type":59,"value":963},{"type":54,"tag":111,"props":1199,"children":1201},{"className":1200},[],[1202],{"type":59,"value":1203},"schedule",{"type":59,"value":963},{"type":54,"tag":111,"props":1206,"children":1208},{"className":1207},[],[1209],{"type":59,"value":1210},"linked_flag",{"type":59,"value":963},{"type":54,"tag":111,"props":1213,"children":1215},{"className":1214},[],[1216],{"type":59,"value":1217},"targeting_flag",{"type":59,"value":963},{"type":54,"tag":111,"props":1220,"children":1222},{"className":1221},[],[1223],{"type":59,"value":1224},"internal_targeting_flag.filters",{"type":59,"value":963},{"type":54,"tag":111,"props":1227,"children":1229},{"className":1228},[],[1230],{"type":59,"value":1231},"responses_limit",{"type":59,"value":963},{"type":54,"tag":111,"props":1234,"children":1236},{"className":1235},[],[1237],{"type":59,"value":1238},"iteration_*",{"type":59,"value":89},{"type":54,"tag":689,"props":1241,"children":1242},{},[1243,1248,1250,1256,1258,1264],{"type":54,"tag":68,"props":1244,"children":1245},{},[1246],{"type":59,"value":1247},"Pull the targeting-flag activity log",{"type":59,"value":1249}," for any \"stopped showing\" ticket. Cohort swaps and rollout changes are invisible in the current config but show up here: ",{"type":54,"tag":111,"props":1251,"children":1253},{"className":1252},[],[1254],{"type":59,"value":1255},"GET \u002Fapi\u002Fprojects\u002F\u003Cid>\u002Factivity_log\u002F?scope=FeatureFlag&item_id=\u003Cflag_id>&limit=20",{"type":59,"value":1257},". Also ",{"type":54,"tag":111,"props":1259,"children":1261},{"className":1260},[],[1262],{"type":59,"value":1263},"?scope=Survey&item_id=\u003Csid>",{"type":59,"value":1265}," to see whether the survey itself was edited.",{"type":54,"tag":689,"props":1267,"children":1268},{},[1269,1274,1276,1282,1284,1290,1292,1297],{"type":54,"tag":68,"props":1270,"children":1271},{},[1272],{"type":59,"value":1273},"Confirm with events.",{"type":59,"value":1275}," Use ",{"type":54,"tag":111,"props":1277,"children":1279},{"className":1278},[],[1280],{"type":59,"value":1281},"$feature_flag_called",{"type":59,"value":1283}," to see what the gating flag actually returned for affected users, and whether ",{"type":54,"tag":111,"props":1285,"children":1287},{"className":1286},[],[1288],{"type":59,"value":1289},"$groups",{"type":59,"value":1291}," is set (see group-aggregation cause below). Use ",{"type":54,"tag":111,"props":1293,"children":1295},{"className":1294},[],[1296],{"type":59,"value":1125},{"type":59,"value":1298}," to see real reach vs the stats UI.",{"type":54,"tag":689,"props":1300,"children":1301},{},[1302,1307],{"type":54,"tag":68,"props":1303,"children":1304},{},[1305],{"type":59,"value":1306},"Diagnose against the known-cause catalog",{"type":59,"value":1308},", confirm with one targeted query, then write the reply.",{"type":54,"tag":91,"props":1310,"children":1312},{"id":1311},"known-cause-catalog",[1313],{"type":59,"value":1314},"Known-cause catalog",{"type":54,"tag":62,"props":1316,"children":1317},{},[1318],{"type":59,"value":1319},"Ordered roughly by how often they're the answer.",{"type":54,"tag":1321,"props":1322,"children":1324},"h3",{"id":1323},"survey-shows-to-fewer-users-than-expected",[1325],{"type":59,"value":1326},"\"Survey shows to fewer users than expected\"",{"type":54,"tag":685,"props":1328,"children":1329},{},[1330,1375,1411],{"type":54,"tag":689,"props":1331,"children":1332},{},[1333,1343,1345,1351,1353,1358,1360,1366,1368,1373],{"type":54,"tag":68,"props":1334,"children":1335},{},[1336,1341],{"type":54,"tag":111,"props":1337,"children":1339},{"className":1338},[],[1340],{"type":59,"value":643},{"type":59,"value":1342}," + URL re-check (web only).",{"type":59,"value":1344}," After the event fires and eligibility passes, the SDK waits N seconds, then re-checks ",{"type":54,"tag":111,"props":1346,"children":1348},{"className":1347},[],[1349],{"type":59,"value":1350},"doesSurveyUrlMatch",{"type":59,"value":1352}," against the ",{"type":54,"tag":76,"props":1354,"children":1355},{},[1356],{"type":59,"value":1357},"current",{"type":59,"value":1359}," URL before rendering (",{"type":54,"tag":111,"props":1361,"children":1363},{"className":1362},[],[1364],{"type":59,"value":1365},"handlePopoverSurvey",{"type":59,"value":1367},"). If the user navigated during the delay, the survey is silently dropped — no ",{"type":54,"tag":111,"props":1369,"children":1371},{"className":1370},[],[1372],{"type":59,"value":1125},{"type":59,"value":1374},". Common on navigation-heavy apps with a non-trivial delay. Fix: lower the delay to 0–2s.",{"type":54,"tag":689,"props":1376,"children":1377},{},[1378,1388,1390,1395,1397,1402,1404,1409],{"type":54,"tag":68,"props":1379,"children":1380},{},[1381,1386],{"type":54,"tag":111,"props":1382,"children":1384},{"className":1383},[],[1385],{"type":59,"value":605},{"type":59,"value":1387}," + the customer's other surveys.",{"type":59,"value":1389}," Any survey shown to a user updates ",{"type":54,"tag":111,"props":1391,"children":1393},{"className":1392},[],[1394],{"type":59,"value":1074},{"type":59,"value":1396},"; this survey is then blocked for the wait window. Completion status is irrelevant. Verify by checking whether the ",{"type":54,"tag":76,"props":1398,"children":1399},{},[1400],{"type":59,"value":1401},"unshown",{"type":59,"value":1403}," cohort saw another survey recently — and confirm against a control group (do the ",{"type":54,"tag":76,"props":1405,"children":1406},{},[1407],{"type":59,"value":1408},"shown",{"type":59,"value":1410}," users differ?). Fix: lower the wait period, or pause competing surveys.",{"type":54,"tag":689,"props":1412,"children":1413},{},[1414,1419,1421,1427],{"type":54,"tag":68,"props":1415,"children":1416},{},[1417],{"type":59,"value":1418},"Cohort composition changed.",{"type":59,"value":1420}," If the survey targets a cohort and someone edited the source dynamic cohort (e.g. added a behavioral filter), every static snapshot taken afterward inherits the narrower definition. Reach drops without any survey-side change. Find it in the flag activity log (cohort swap) and confirm cohort sizes via ",{"type":54,"tag":111,"props":1422,"children":1424},{"className":1423},[],[1425],{"type":59,"value":1426},"static_cohort_people",{"type":59,"value":89},{"type":54,"tag":1321,"props":1429,"children":1431},{"id":1430},"event-based-survey-never-fires",[1432],{"type":59,"value":1433},"\"Event-based survey never fires\"",{"type":54,"tag":685,"props":1435,"children":1436},{},[1437,1455,1538,1585],{"type":54,"tag":689,"props":1438,"children":1439},{},[1440,1445,1447,1453],{"type":54,"tag":68,"props":1441,"children":1442},{},[1443],{"type":59,"value":1444},"Timing race at session start.",{"type":59,"value":1446}," Event captured before ",{"type":54,"tag":111,"props":1448,"children":1450},{"className":1449},[],[1451],{"type":59,"value":1452},"\u002Fapi\u002Fsurveys",{"type":59,"value":1454}," returns and the capture hook registers. Signature: event fires very early in session. Unavoidable client-side; mitigate by triggering on a slightly later event.",{"type":54,"tag":689,"props":1456,"children":1457},{},[1458,1470,1472,1477,1479,1485,1487,1492,1494,1500,1502,1507,1509,1514,1516,1521,1523,1528,1530,1536],{"type":54,"tag":68,"props":1459,"children":1460},{},[1461,1463,1468],{"type":59,"value":1462},"Group-aggregated ",{"type":54,"tag":111,"props":1464,"children":1466},{"className":1465},[],[1467],{"type":59,"value":1210},{"type":59,"value":1469}," with no group context.",{"type":59,"value":1471}," If ",{"type":54,"tag":111,"props":1473,"children":1475},{"className":1474},[],[1476],{"type":59,"value":1210},{"type":59,"value":1478}," (or targeting flag) has ",{"type":54,"tag":111,"props":1480,"children":1482},{"className":1481},[],[1483],{"type":59,"value":1484},"aggregation_group_type_index",{"type":59,"value":1486}," set, it evaluates against a ",{"type":54,"tag":76,"props":1488,"children":1489},{},[1490],{"type":59,"value":1491},"group",{"type":59,"value":1493},", not the person. Without ",{"type":54,"tag":111,"props":1495,"children":1497},{"className":1496},[],[1498],{"type":59,"value":1499},"posthog.group(\u003Cindex>, \u003Ckey>)",{"type":59,"value":1501}," set before the event fires, the flag returns ",{"type":54,"tag":68,"props":1503,"children":1504},{},[1505],{"type":59,"value":1506},"false",{"type":59,"value":1508}," and the survey never shows. Signature: ",{"type":54,"tag":111,"props":1510,"children":1512},{"className":1511},[],[1513],{"type":59,"value":1281},{"type":59,"value":1515}," returns ",{"type":54,"tag":111,"props":1517,"children":1519},{"className":1518},[],[1520],{"type":59,"value":1506},{"type":59,"value":1522}," with empty ",{"type":54,"tag":111,"props":1524,"children":1526},{"className":1525},[],[1527],{"type":59,"value":1289},{"type":59,"value":1529},", and the ",{"type":54,"tag":111,"props":1531,"children":1533},{"className":1532},[],[1534],{"type":59,"value":1535},"$feature\u002F\u003Ckey>",{"type":59,"value":1537}," property is missing from the trigger events. Fix: set group context in the SDK, or switch the survey to a person-level flag.",{"type":54,"tag":689,"props":1539,"children":1540},{},[1541,1546,1548,1553,1555,1560,1562,1567,1569,1575,1577,1583],{"type":54,"tag":68,"props":1542,"children":1543},{},[1544],{"type":59,"value":1545},"Customer wired the survey to the wrong flag.",{"type":59,"value":1547}," They create a flag with email\u002Fproperty targeting but the survey's ",{"type":54,"tag":111,"props":1549,"children":1551},{"className":1550},[],[1552],{"type":59,"value":1210},{"type":59,"value":1554},"\u002F",{"type":54,"tag":111,"props":1556,"children":1558},{"className":1557},[],[1559],{"type":59,"value":1217},{"type":59,"value":1561}," points at a ",{"type":54,"tag":76,"props":1563,"children":1564},{},[1565],{"type":59,"value":1566},"different",{"type":59,"value":1568}," flag. Always confirm the actual ",{"type":54,"tag":111,"props":1570,"children":1572},{"className":1571},[],[1573],{"type":59,"value":1574},"linked_flag.key",{"type":59,"value":1576}," \u002F ",{"type":54,"tag":111,"props":1578,"children":1580},{"className":1579},[],[1581],{"type":59,"value":1582},"targeting_flag.key",{"type":59,"value":1584}," from the API — don't trust the customer's description.",{"type":54,"tag":689,"props":1586,"children":1587},{},[1588,1593,1595,1601,1603,1609,1610,1616,1618,1624,1626,1631],{"type":54,"tag":68,"props":1589,"children":1590},{},[1591],{"type":59,"value":1592},"Behavioral cohort in a realtime flag.",{"type":59,"value":1594}," A cohort with ",{"type":54,"tag":111,"props":1596,"children":1598},{"className":1597},[],[1599],{"type":59,"value":1600},"performed_event",{"type":59,"value":1602},"\u002Fbehavioral filters can't be evaluated in realtime flag bytecode (",{"type":54,"tag":111,"props":1604,"children":1606},{"className":1605},[],[1607],{"type":59,"value":1608},"\"Unsupported behavioral filter for realtime bytecode\"",{"type":59,"value":963},{"type":54,"tag":111,"props":1611,"children":1613},{"className":1612},[],[1614],{"type":59,"value":1615},"posthog\u002Fapi\u002Fcohort.py",{"type":59,"value":1617},"). The cohort shows a ",{"type":54,"tag":111,"props":1619,"children":1621},{"className":1620},[],[1622],{"type":59,"value":1623},"bytecode_error",{"type":59,"value":1625},". Surveys\u002Fflags can't use it directly — the customer must make a ",{"type":54,"tag":76,"props":1627,"children":1628},{},[1629],{"type":59,"value":1630},"static",{"type":59,"value":1632}," copy of the cohort and target that.",{"type":54,"tag":1321,"props":1634,"children":1636},{"id":1635},"responses-show-as-zero-in-the-ui-but-raw-events-exist",[1637],{"type":59,"value":1638},"\"Responses show as zero in the UI but raw events exist\"",{"type":54,"tag":685,"props":1640,"children":1641},{},[1642],{"type":54,"tag":689,"props":1643,"children":1644},{},[1645,1650,1652,1658,1660,1666,1668,1674,1675,1681,1683,1689,1691,1697,1699,1705,1707,1713,1715,1720,1722,1728,1730,1736,1738,1742],{"type":54,"tag":68,"props":1646,"children":1647},{},[1648],{"type":59,"value":1649},"Max AI corrupted the survey definition.",{"type":59,"value":1651}," Max's ",{"type":54,"tag":111,"props":1653,"children":1655},{"className":1654},[],[1656],{"type":59,"value":1657},"edit_survey",{"type":59,"value":1659}," tool (",{"type":54,"tag":111,"props":1661,"children":1663},{"className":1662},[],[1664],{"type":59,"value":1665},"products\u002Fsurveys\u002Fbackend\u002Fmax_tools.py",{"type":59,"value":1667},") has two failure modes: (a) on reorder\u002Fedit it rebuilds each question from ",{"type":54,"tag":111,"props":1669,"children":1671},{"className":1670},[],[1672],{"type":59,"value":1673},"QUESTION_TYPE_MAP",{"type":59,"value":531},{"type":54,"tag":111,"props":1676,"children":1678},{"className":1677},[],[1679],{"type":59,"value":1680},"nps",{"type":59,"value":1682},"→scale 10, ",{"type":54,"tag":111,"props":1684,"children":1686},{"className":1685},[],[1687],{"type":59,"value":1688},"csat",{"type":59,"value":1690},"→scale 5, etc.), so picking the wrong semantic type silently changes a question's scale; (b) the ",{"type":54,"tag":111,"props":1692,"children":1694},{"className":1693},[],[1695],{"type":59,"value":1696},"id",{"type":59,"value":1698}," field expects 1-indexed labels (",{"type":54,"tag":111,"props":1700,"children":1702},{"className":1701},[],[1703],{"type":59,"value":1704},"\"1\"",{"type":59,"value":1706},",",{"type":54,"tag":111,"props":1708,"children":1710},{"className":1709},[],[1711],{"type":59,"value":1712},"\"2\"",{"type":59,"value":1714},") — passing a real UUID falls through and a ",{"type":54,"tag":76,"props":1716,"children":1717},{},[1718],{"type":59,"value":1719},"fresh",{"type":59,"value":1721}," UUID is generated, so responses keyed by ",{"type":54,"tag":111,"props":1723,"children":1725},{"className":1724},[],[1726],{"type":59,"value":1727},"$survey_response_\u003Cold_uuid>",{"type":59,"value":1729}," no longer join to the question. Raw events are intact; only the definition is wrong. Fix: PATCH the ",{"type":54,"tag":111,"props":1731,"children":1733},{"className":1732},[],[1734],{"type":59,"value":1735},"questions",{"type":59,"value":1737}," array back to the original UUIDs (recoverable from the response events) and restore the question type. Tell the customer to edit question ",{"type":54,"tag":76,"props":1739,"children":1740},{},[1741],{"type":59,"value":59},{"type":59,"value":1743}," via the UI, and avoid asking Max to reorder questions on a survey with historical responses until the tool guards UUIDs.",{"type":54,"tag":1321,"props":1745,"children":1747},{"id":1746},"cohort-count-shows-0-but-the-cohort-is-populated",[1748],{"type":59,"value":1749},"\"Cohort count shows 0 but the cohort is populated\"",{"type":54,"tag":685,"props":1751,"children":1752},{},[1753],{"type":54,"tag":689,"props":1754,"children":1755},{},[1756,1758,1762,1764,1769,1771,1775,1777,1783,1785,1791,1793,1799],{"type":59,"value":1757},"Cosmetic UI bug, does ",{"type":54,"tag":68,"props":1759,"children":1760},{},[1761],{"type":59,"value":762},{"type":59,"value":1763}," affect targeting. Confirm the real count via ",{"type":54,"tag":111,"props":1765,"children":1767},{"className":1766},[],[1768],{"type":59,"value":1426},{"type":59,"value":1770},". NOTE: this is ",{"type":54,"tag":76,"props":1772,"children":1773},{},[1774],{"type":59,"value":762},{"type":59,"value":1776}," a simple one-line bug — the normal ",{"type":54,"tag":111,"props":1778,"children":1780},{"className":1779},[],[1781],{"type":59,"value":1782},"insert_cohort_from_query",{"type":59,"value":1784}," path does recompute count via ",{"type":54,"tag":111,"props":1786,"children":1788},{"className":1787},[],[1789],{"type":59,"value":1790},"count_cohort_members",{"type":59,"value":1792},"; the ",{"type":54,"tag":111,"props":1794,"children":1796},{"className":1795},[],[1797],{"type":59,"value":1798},"count=0",{"type":59,"value":1800}," display only appears on certain failure paths. Do not promise a quick fix without reproducing the specific path.",{"type":54,"tag":1321,"props":1802,"children":1804},{"id":1803},"general-caution",[1805],{"type":59,"value":1806},"General caution",{"type":54,"tag":685,"props":1808,"children":1809},{},[1810,1820],{"type":54,"tag":689,"props":1811,"children":1812},{},[1813,1818],{"type":54,"tag":68,"props":1814,"children":1815},{},[1816],{"type":59,"value":1817},"Aged tickets are dirty.",{"type":59,"value":1819}," Config may have been edited by the customer or a prior agent during troubleshooting. Pull activity logs; frame secondary findings as \"while you're in there, double-check X\" rather than \"we found X is broken.\"",{"type":54,"tag":689,"props":1821,"children":1822},{},[1823,1835],{"type":54,"tag":68,"props":1824,"children":1825},{},[1826,1828,1833],{"type":59,"value":1827},"The stats UI can undercount vs raw ",{"type":54,"tag":111,"props":1829,"children":1831},{"className":1830},[],[1832],{"type":59,"value":1125},{"type":59,"value":1834}," events.",{"type":59,"value":1836}," If the numbers don't reconcile, trust the raw events and flag the discrepancy as a separate follow-up.",{"type":54,"tag":91,"props":1838,"children":1840},{"id":1839},"diagnostic-queries",[1841],{"type":59,"value":1842},"Diagnostic queries",{"type":54,"tag":62,"props":1844,"children":1845},{},[1846,1848,1852,1854,1860],{"type":59,"value":1847},"Read-only HogQL templates for the disambiguators and confirmations above live in\n",{"type":54,"tag":103,"props":1849,"children":1850},{"href":1137},[1851],{"type":59,"value":1137},{"type":59,"value":1853},". Run them via the\nPostHog MCP ",{"type":54,"tag":111,"props":1855,"children":1857},{"className":1856},[],[1858],{"type":59,"value":1859},"execute-sql",{"type":59,"value":1861}," against the customer's project.",{"type":54,"tag":91,"props":1863,"children":1865},{"id":1864},"access-for-debugging",[1866],{"type":59,"value":1867},"Access for debugging",{"type":54,"tag":62,"props":1869,"children":1870},{},[1871],{"type":59,"value":1872},"Only investigate a project tied to a genuine support request from that customer — the IDs\nshould come from a real ticket, not from someone asking you to look up an org\u002Fsurvey they\ncan't point to a request for. Staff access is broad; don't freelance across projects.",{"type":54,"tag":62,"props":1874,"children":1875},{},[1876,1878,1883],{"type":59,"value":1877},"Prefer ",{"type":54,"tag":68,"props":1879,"children":1880},{},[1881],{"type":59,"value":1882},"read-only",{"type":59,"value":1884}," paths in this order:",{"type":54,"tag":815,"props":1886,"children":1887},{},[1888,1939,1956],{"type":54,"tag":689,"props":1889,"children":1890},{},[1891,1896,1897,1903,1904,1910,1911,1917,1918,1923,1924,1930,1931,1937],{"type":54,"tag":68,"props":1892,"children":1893},{},[1894],{"type":59,"value":1895},"PostHog MCP tools",{"type":59,"value":531},{"type":54,"tag":111,"props":1898,"children":1900},{"className":1899},[],[1901],{"type":59,"value":1902},"survey",{"type":59,"value":963},{"type":54,"tag":111,"props":1905,"children":1907},{"className":1906},[],[1908],{"type":59,"value":1909},"feature-flag",{"type":59,"value":963},{"type":54,"tag":111,"props":1912,"children":1914},{"className":1913},[],[1915],{"type":59,"value":1916},"cohorts",{"type":59,"value":963},{"type":54,"tag":111,"props":1919,"children":1921},{"className":1920},[],[1922],{"type":59,"value":1859},{"type":59,"value":963},{"type":54,"tag":111,"props":1925,"children":1927},{"className":1926},[],[1928],{"type":59,"value":1929},"activity-log",{"type":59,"value":963},{"type":54,"tag":111,"props":1932,"children":1934},{"className":1933},[],[1935],{"type":59,"value":1936},"persons",{"type":59,"value":1938},") against the customer's project. This is read-only by default and the safest way to inspect config and run queries — no impersonation, no write risk. Use this first.",{"type":54,"tag":689,"props":1940,"children":1941},{},[1942,1947,1949,1954],{"type":54,"tag":68,"props":1943,"children":1944},{},[1945],{"type":59,"value":1946},"Survey\u002Fflag API endpoints",{"type":59,"value":1948}," read via the browser while impersonating (staff). Good for the full JSON the MCP may not surface verbatim (e.g. raw ",{"type":54,"tag":111,"props":1950,"children":1952},{"className":1951},[],[1953],{"type":59,"value":1224},{"type":59,"value":1955},").",{"type":54,"tag":689,"props":1957,"children":1958},{},[1959,1964],{"type":54,"tag":68,"props":1960,"children":1961},{},[1962],{"type":59,"value":1963},"Django admin",{"type":59,"value":1965}," only when 1 and 2 can't answer it. It's powerful and write-capable, so treat it as read-only by discipline: look, don't change. Never edit a customer's survey\u002Fflag\u002Fcohort from admin without explicit customer consent.",{"type":54,"tag":62,"props":1967,"children":1968},{},[1969],{"type":59,"value":1970},"When you need a value the MCP can't infer (project ID, instance, which survey), ask the\noperator to paste the survey API JSON — it skips several round-trips.",{"type":54,"tag":91,"props":1972,"children":1974},{"id":1973},"writing-the-customer-reply",[1975],{"type":59,"value":1976},"Writing the customer reply",{"type":54,"tag":62,"props":1978,"children":1979},{},[1980],{"type":59,"value":1981},"Voice derived from the PostHog handbook support values (reassuringly human, humble, ship\nfixes, clear with no jargon). Rules:",{"type":54,"tag":685,"props":1983,"children":1984},{},[1985,1995,2005,2029,2063,2073,2083,2093,2103],{"type":54,"tag":689,"props":1986,"children":1987},{},[1988,1993],{"type":54,"tag":68,"props":1989,"children":1990},{},[1991],{"type":59,"value":1992},"Lead with the cause, then the fix.",{"type":59,"value":1994}," One line on what's happening, then what to do.",{"type":54,"tag":689,"props":1996,"children":1997},{},[1998,2003],{"type":54,"tag":68,"props":1999,"children":2000},{},[2001],{"type":59,"value":2002},"Bold the issue and bold each action.",{"type":59,"value":2004}," Make the problem and the next step scannable.",{"type":54,"tag":689,"props":2006,"children":2007},{},[2008,2013,2015,2020,2022,2027],{"type":54,"tag":68,"props":2009,"children":2010},{},[2011],{"type":59,"value":2012},"Use the labels the customer sees in the UI,",{"type":59,"value":2014}," never internal field names. Grep ",{"type":54,"tag":111,"props":2016,"children":2018},{"className":2017},[],[2019],{"type":59,"value":191},{"type":59,"value":2021}," for the real string. E.g. ",{"type":54,"tag":111,"props":2023,"children":2025},{"className":2024},[],[2026],{"type":59,"value":643},{"type":59,"value":2028}," → \"Delay survey popup by at least N seconds once the display conditions are met\"; the wait period is \"Survey wait period\" \u002F \"Don't show this survey if another one was shown to the user in the last N days\". The customer's own event names stay verbatim.",{"type":54,"tag":689,"props":2030,"children":2031},{},[2032,2037,2039,2045,2047,2053,2055,2061],{"type":54,"tag":68,"props":2033,"children":2034},{},[2035],{"type":59,"value":2036},"Link every PostHog entity",{"type":59,"value":2038}," by ID. Cohorts: ",{"type":54,"tag":111,"props":2040,"children":2042},{"className":2041},[],[2043],{"type":59,"value":2044},"https:\u002F\u002F\u003Cus|eu>.posthog.com\u002Fproject\u002F\u003Cid>\u002Fcohorts\u002F\u003Ccohort_id>",{"type":59,"value":2046},". Flags: ",{"type":54,"tag":111,"props":2048,"children":2050},{"className":2049},[],[2051],{"type":59,"value":2052},"...\u002Ffeature_flags\u002F\u003Cflag_id>",{"type":59,"value":2054},". Surveys: ",{"type":54,"tag":111,"props":2056,"children":2058},{"className":2057},[],[2059],{"type":59,"value":2060},"...\u002Fsurveys\u002F\u003Csurvey_id>",{"type":59,"value":2062},". Match the customer's instance (US vs EU).",{"type":54,"tag":689,"props":2064,"children":2065},{},[2066,2071],{"type":54,"tag":68,"props":2067,"children":2068},{},[2069],{"type":59,"value":2070},"Predict the expected outcome",{"type":59,"value":2072}," so the customer can verify the fix worked (\"you should see ~X going forward\").",{"type":54,"tag":689,"props":2074,"children":2075},{},[2076,2081],{"type":54,"tag":68,"props":2077,"children":2078},{},[2079],{"type":59,"value":2080},"Gauge the customer's technical level.",{"type":59,"value":2082}," If they can run SQL and hit the API, offer the patch path. If not, offer to apply the fix ourselves (and confirm any destructive detail first).",{"type":54,"tag":689,"props":2084,"children":2085},{},[2086,2091],{"type":54,"tag":68,"props":2087,"children":2088},{},[2089],{"type":59,"value":2090},"Do NOT offer to \"hop on a call\" or book a meeting.",{"type":59,"value":2092}," PostHog support is async-first. Close with \"We're always here if you need a follow-up.\"",{"type":54,"tag":689,"props":2094,"children":2095},{},[2096,2101],{"type":54,"tag":68,"props":2097,"children":2098},{},[2099],{"type":59,"value":2100},"Never leak internals",{"type":59,"value":2102}," — no MCP tool names, code paths, line numbers, Django admin, staff impersonation, or other customers. Keep it to product concepts a customer recognizes.",{"type":54,"tag":689,"props":2104,"children":2105},{},[2106,2111,2113,2119],{"type":54,"tag":68,"props":2107,"children":2108},{},[2109],{"type":59,"value":2110},"Run the final draft through a humanizer skill before sending",{"type":59,"value":2112}," (if you have one, e.g. ",{"type":54,"tag":111,"props":2114,"children":2116},{"className":2115},[],[2117],{"type":59,"value":2118},"humanizer",{"type":59,"value":2120},"). Strip em dashes, setup phrases (\"Here's the thing:\", \"Three things to know\"), rule-of-three padding, and tidy parallel list structure. The reply should read like a person typed it.",{"type":54,"tag":62,"props":2122,"children":2123},{},[2124],{"type":59,"value":2125},"Reply skeleton:",{"type":54,"tag":2127,"props":2128,"children":2133},"pre",{"className":2129,"code":2131,"language":59,"meta":2132},[2130],"language-text","Hi \u003Cname>,\n\n\u003Cone line: tracked it down + the cause in plain terms.>\n\n**The problem:** \u003Cwhat's happening, with the evidence you pulled.>\n\n**\u003CFastest fix \u002F two ways to fix it>:**\n1. **\u003Caction>** — \u003Cwhy \u002F how.>\n2. **\u003Caction>** — \u003Cwhy \u002F how.>\n\n**Also worth knowing while you're in there:** \u003Csecondary finding, softened.>\n\nWe're always here if you need a follow-up.\n","",[2134],{"type":54,"tag":111,"props":2135,"children":2136},{"__ignoreMap":2132},[2137],{"type":59,"value":2131},{"type":54,"tag":91,"props":2139,"children":2141},{"id":2140},"feature-work-shipping-across-sdks",[2142],{"type":59,"value":2143},"Feature work — shipping across SDKs",{"type":54,"tag":62,"props":2145,"children":2146},{},[2147],{"type":59,"value":2148},"A survey capability is only \"done\" when it works (or is deliberately scoped out) on every\nSDK a customer might use. When building or changing survey behavior:",{"type":54,"tag":815,"props":2150,"children":2151},{},[2152,2163,2175,2209,2229],{"type":54,"tag":689,"props":2153,"children":2154},{},[2155,2157,2162],{"type":59,"value":2156},"Land the backend\u002FUI change in this repo (serializer + ",{"type":54,"tag":111,"props":2158,"children":2160},{"className":2159},[],[2161],{"type":59,"value":191},{"type":59,"value":1955},{"type":54,"tag":689,"props":2164,"children":2165},{},[2166,2168,2173],{"type":59,"value":2167},"Decide the per-SDK story using the parity table. If a feature lands web-only (like\n",{"type":54,"tag":111,"props":2169,"children":2171},{"className":2170},[],[2172],{"type":59,"value":643},{"type":59,"value":2174},"), say so explicitly in the docs and the PR — silent gaps\nbecome support tickets.",{"type":54,"tag":689,"props":2176,"children":2177},{},[2178,2180,2186,2188,2194,2195,2201,2203,2207],{"type":59,"value":2179},"Implement in the SDK repos (",{"type":54,"tag":111,"props":2181,"children":2183},{"className":2182},[],[2184],{"type":59,"value":2185},"posthog-js",{"type":59,"value":2187}," covers both web and React Native), then\n",{"type":54,"tag":111,"props":2189,"children":2191},{"className":2190},[],[2192],{"type":59,"value":2193},"posthog-ios",{"type":59,"value":963},{"type":54,"tag":111,"props":2196,"children":2198},{"className":2197},[],[2199],{"type":59,"value":2200},"posthog-android",{"type":59,"value":2202},", and the Flutter Dart layer. Remember Flutter's split:\neligibility\u002Ftrigger logic is native (iOS\u002FAndroid), rendering is Dart. Use the registry\nin ",{"type":54,"tag":103,"props":2204,"children":2205},{"href":105},[2206],{"type":59,"value":105},{"type":59,"value":2208}," to find each checkout.",{"type":54,"tag":689,"props":2210,"children":2211},{},[2212,2214,2220,2222,2227],{"type":59,"value":2213},"Update the ",{"type":54,"tag":111,"props":2215,"children":2217},{"className":2216},[],[2218],{"type":59,"value":2219},"posthog.com",{"type":59,"value":2221}," docs (",{"type":54,"tag":111,"props":2223,"children":2225},{"className":2224},[],[2226],{"type":59,"value":350},{"type":59,"value":2228},") and this parity table.",{"type":54,"tag":689,"props":2230,"children":2231},{},[2232,2234,2239],{"type":59,"value":2233},"Use the ",{"type":54,"tag":111,"props":2235,"children":2237},{"className":2236},[],[2238],{"type":59,"value":785},{"type":59,"value":2240}," skill (if available) to confirm version requirements and cross-SDK coverage.",{"items":2242,"total":2406},[2243,2256,2268,2280,2293,2306,2322,2339,2353,2368,2378,2396],{"slug":2244,"name":2244,"fn":2245,"description":2246,"org":2247,"tags":2248,"stars":23,"repoUrl":24,"updatedAt":2255},"analyzing-expensive-users","analyze expensive users in AI observability","Analyze the most expensive users in AI observability and explain why they cost so much. Use when the user asks about top spenders, expensive users, per-user LLM cost, user-level cost drivers, or patterns behind high AI observability spend.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2249,2250,2253,2254],{"name":18,"slug":19,"type":15},{"name":2251,"slug":2252,"type":15},"Cost Optimization","cost-optimization",{"name":13,"slug":14,"type":15},{"name":9,"slug":8,"type":15},"2026-07-28T05:34:11.117757",{"slug":2257,"name":2257,"fn":2258,"description":2259,"org":2260,"tags":2261,"stars":23,"repoUrl":24,"updatedAt":2267},"auditing-endpoints","audit PostHog project endpoints","Audit every endpoint in a PostHog project for staleness, failed materialisations, and unused materialised versions. Use when the user asks \"what endpoints can I clean up?\", \"are any of my endpoints broken?\", \"which materialised versions are still being called?\", or wants a one-shot cleanup pass over the Endpoints product. Produces a prioritised report grouped by issue type, with recommended actions but does not modify anything without explicit confirmation.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2262,2263,2266],{"name":18,"slug":19,"type":15},{"name":2264,"slug":2265,"type":15},"Audit","audit",{"name":9,"slug":8,"type":15},"2026-06-08T08:08:33.693989",{"slug":2269,"name":2269,"fn":2270,"description":2271,"org":2272,"tags":2273,"stars":23,"repoUrl":24,"updatedAt":2279},"auditing-warehouse-source-health","audit PostHog data warehouse source health","Audit the health of a PostHog project's data warehouse sources and syncs — find every broken or degraded source connection, sync schema, and webhook channel. Use when the user asks \"why are my imports failing?\", \"what's broken with my sources?\", \"why is my warehouse data stale?\", or wants a one-shot triage of source\u002Fsync health before deciding where to dig in. Produces a prioritized report grouped by severity, with recommended next steps. For materialized-view health use `auditing-warehouse-view-health`; for a single failing sync use `diagnosing-failed-warehouse-syncs`.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2274,2275,2277,2278],{"name":2264,"slug":2265,"type":15},{"name":2276,"slug":32,"type":15},"Data Warehouse",{"name":13,"slug":14,"type":15},{"name":9,"slug":8,"type":15},"2026-06-18T08:22:57.67984",{"slug":2281,"name":2281,"fn":2282,"description":2283,"org":2284,"tags":2285,"stars":23,"repoUrl":24,"updatedAt":2292},"auditing-warehouse-view-health","audit PostHog materialized view health","Audit the health of a PostHog project's materialized views (saved queries) — find every failed materialization and flag unused or stale materialized views that cost storage and compute. Use when the user asks \"which of my views are broken?\", \"why is this materialized view failing?\", \"are any of my views wasting compute?\", or wants a one-shot triage of view health. For source\u002Fsync health use `auditing-warehouse-source-health`.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2286,2287,2288,2291],{"name":2264,"slug":2265,"type":15},{"name":2276,"slug":32,"type":15},{"name":2289,"slug":2290,"type":15},"Performance","performance",{"name":9,"slug":8,"type":15},"2026-06-18T08:25:10.936787",{"slug":2294,"name":2294,"fn":2295,"description":2296,"org":2297,"tags":2298,"stars":23,"repoUrl":24,"updatedAt":2305},"authoring-error-tracking-alerts","author PostHog error tracking alerts","Author error tracking alerts that fire when an issue is created, reopened, or starts spiking. Use when the user asks to set up error notifications, route exceptions to Slack\u002Fwebhook\u002FLinear, or evaluate which error events are worth alerting on. Covers trigger-event selection, integration choice, dedup against existing alerts, and shipping with the canonical message body shape.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2299,2302,2303,2304],{"name":2300,"slug":2301,"type":15},"Alerting","alerting",{"name":21,"slug":22,"type":15},{"name":13,"slug":14,"type":15},{"name":9,"slug":8,"type":15},"2026-06-18T08:24:40.318583",{"slug":2307,"name":2307,"fn":2308,"description":2309,"org":2310,"tags":2311,"stars":23,"repoUrl":24,"updatedAt":2321},"authoring-log-alerts","author log alerts in PostHog","Author useful, low-noise log alerts on services in a PostHog project. Use when the user asks to set up alerts for their logs, suggest alerts they should add, or evaluate whether a service is worth monitoring. Covers service triage, baseline characterisation, threshold drafting, back-testing via simulate, and shipping with a notification destination.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2312,2313,2316,2317,2320],{"name":18,"slug":19,"type":15},{"name":2314,"slug":2315,"type":15},"Monitoring","monitoring",{"name":13,"slug":14,"type":15},{"name":2318,"slug":2319,"type":15},"Operations","operations",{"name":9,"slug":8,"type":15},"2026-07-18T05:10:54.430898",{"slug":2323,"name":2323,"fn":2324,"description":2325,"org":2326,"tags":2327,"stars":23,"repoUrl":24,"updatedAt":2338},"building-workflows","build and edit PostHog workflows","Build, edit, test, enable, and monitor PostHog workflows over MCP. Author the action\u002Fedge graph so it runs and opens cleanly in the visual editor, then change drafts surgically with patch operations. Use when asked to build, set up, automate, change, fix, or debug a workflow, campaign, broadcast, drip sequence, or event-triggered automation in the workflows product.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2328,2331,2334,2335],{"name":2329,"slug":2330,"type":15},"Automation","automation",{"name":2332,"slug":2333,"type":15},"MCP","mcp",{"name":9,"slug":8,"type":15},{"name":2336,"slug":2337,"type":15},"Workflow Automation","workflow-automation","2026-07-28T05:34:12.167015",{"slug":2340,"name":2340,"fn":2341,"description":2342,"org":2343,"tags":2344,"stars":23,"repoUrl":24,"updatedAt":2352},"check-posthog-loading","inspect PostHog SDK loading across URLs","Inspect how the PostHog JavaScript SDK is loaded across a list of URLs. Use to confirm consistent installation across pages, find pages missing the snippet, detect mismatched API keys or hosts between pages, and verify the load method (head snippet vs deferred vs array.js).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2345,2346,2347,2350,2351],{"name":18,"slug":19,"type":15},{"name":21,"slug":22,"type":15},{"name":2348,"slug":2349,"type":15},"Frontend","frontend",{"name":13,"slug":14,"type":15},{"name":9,"slug":8,"type":15},"2026-05-07T05:56:19.828048",{"slug":2354,"name":2354,"fn":2355,"description":2356,"org":2357,"tags":2358,"stars":23,"repoUrl":24,"updatedAt":2367},"consuming-endpoints-from-client-code","integrate PostHog endpoints into client applications","Wire a PostHog endpoint into a client app or SDK. Covers fetching the OpenAPI spec, generating a typed client with openapi-generator or @hey-api\u002Fopenapi-ts, sending the right auth header, shaping the variables payload (HogQL code_name vs insight breakdown property), handling rate-limit and materialised-endpoint error responses. Use when the user says \"how do I call my endpoint\", \"generate a client for this\", or \"what auth header do I use\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2359,2362,2363,2364],{"name":2360,"slug":2361,"type":15},"API Development","api-development",{"name":2348,"slug":2349,"type":15},{"name":9,"slug":8,"type":15},{"name":2365,"slug":2366,"type":15},"SDK","sdk","2026-06-08T08:08:34.929454",{"slug":2369,"name":2369,"fn":2370,"description":2371,"org":2372,"tags":2373,"stars":23,"repoUrl":24,"updatedAt":2377},"copying-endpoints-across-projects","copy PostHog endpoints across projects","Copy a PostHog endpoint (a saved HogQL\u002Finsight query exposed as an API route) to another project in the same organization, or duplicate it under a new name in the same project. Use when the user wants to duplicate an endpoint, promote an endpoint from staging to production, replicate an endpoint's query\u002Fvariables\u002Ffreshness config in another workspace, or clone an endpoint to iterate on it. Unlike feature flags and experiments, endpoints have NO native cross-project copy tool — this skill covers the read-then-recreate flow (endpoint-get then endpoint-create), the active-project switching it requires, name-collision checks, and the safe defaults (land unmaterialised in the target, verify with endpoint-run). Does not cover editing endpoint versions (see managing-endpoint-versions) or authoring a brand-new endpoint from scratch (see creating-an-endpoint).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2374,2375,2376],{"name":2360,"slug":2361,"type":15},{"name":2318,"slug":2319,"type":15},{"name":9,"slug":8,"type":15},"2026-07-15T05:29:58.442727",{"slug":2379,"name":2379,"fn":2380,"description":2381,"org":2382,"tags":2383,"stars":23,"repoUrl":24,"updatedAt":2395},"creating-ai-subscription","schedule recurring AI-generated PostHog reports","Create a recurring AI-generated PostHog report — schedule a free-text prompt to run on a cron, with the LLM-synthesized markdown delivered to email or Slack on each tick. Use when the user wants a recurring AI summary of X on any cadence (daily, weekly, monthly, yearly) rather than a one-off report. (To attach an AI summary to an existing insight\u002Fdashboard subscription instead of a free-text prompt, see `managing-subscriptions` and its `summary_enabled` option.)\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2384,2385,2388,2389,2392],{"name":2329,"slug":2330,"type":15},{"name":2386,"slug":2387,"type":15},"Email","email",{"name":9,"slug":8,"type":15},{"name":2390,"slug":2391,"type":15},"Reporting","reporting",{"name":2393,"slug":2394,"type":15},"Slack","slack","2026-06-09T07:32:27.935712",{"slug":2397,"name":2397,"fn":2398,"description":2399,"org":2400,"tags":2401,"stars":23,"repoUrl":24,"updatedAt":2405},"creating-an-endpoint","create PostHog API endpoints","Create a PostHog endpoint with the right shape on the first try — covers query kind choice, name conventions, what to expose as variables (HogQL code_name vs insight breakdown), data_freshness_seconds, and whether to materialise on day one. Use when the user says \"create an endpoint\", \"expose this query as an API\", \"turn this insight into an endpoint\", or asks for help structuring a new endpoint. Steers away from common mistakes: materialising a query with cohort breakdowns or compare mode, inline-only variables on a materialised endpoint, unbounded date ranges, ambiguous names.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2402,2403,2404],{"name":18,"slug":19,"type":15},{"name":2360,"slug":2361,"type":15},{"name":9,"slug":8,"type":15},"2026-06-08T08:08:29.624498",231,{"items":2408,"total":2458},[2409,2416,2422,2429,2436,2443,2451],{"slug":2244,"name":2244,"fn":2245,"description":2246,"org":2410,"tags":2411,"stars":23,"repoUrl":24,"updatedAt":2255},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2412,2413,2414,2415],{"name":18,"slug":19,"type":15},{"name":2251,"slug":2252,"type":15},{"name":13,"slug":14,"type":15},{"name":9,"slug":8,"type":15},{"slug":2257,"name":2257,"fn":2258,"description":2259,"org":2417,"tags":2418,"stars":23,"repoUrl":24,"updatedAt":2267},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2419,2420,2421],{"name":18,"slug":19,"type":15},{"name":2264,"slug":2265,"type":15},{"name":9,"slug":8,"type":15},{"slug":2269,"name":2269,"fn":2270,"description":2271,"org":2423,"tags":2424,"stars":23,"repoUrl":24,"updatedAt":2279},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2425,2426,2427,2428],{"name":2264,"slug":2265,"type":15},{"name":2276,"slug":32,"type":15},{"name":13,"slug":14,"type":15},{"name":9,"slug":8,"type":15},{"slug":2281,"name":2281,"fn":2282,"description":2283,"org":2430,"tags":2431,"stars":23,"repoUrl":24,"updatedAt":2292},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2432,2433,2434,2435],{"name":2264,"slug":2265,"type":15},{"name":2276,"slug":32,"type":15},{"name":2289,"slug":2290,"type":15},{"name":9,"slug":8,"type":15},{"slug":2294,"name":2294,"fn":2295,"description":2296,"org":2437,"tags":2438,"stars":23,"repoUrl":24,"updatedAt":2305},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2439,2440,2441,2442],{"name":2300,"slug":2301,"type":15},{"name":21,"slug":22,"type":15},{"name":13,"slug":14,"type":15},{"name":9,"slug":8,"type":15},{"slug":2307,"name":2307,"fn":2308,"description":2309,"org":2444,"tags":2445,"stars":23,"repoUrl":24,"updatedAt":2321},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2446,2447,2448,2449,2450],{"name":18,"slug":19,"type":15},{"name":2314,"slug":2315,"type":15},{"name":13,"slug":14,"type":15},{"name":2318,"slug":2319,"type":15},{"name":9,"slug":8,"type":15},{"slug":2323,"name":2323,"fn":2324,"description":2325,"org":2452,"tags":2453,"stars":23,"repoUrl":24,"updatedAt":2338},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2454,2455,2456,2457],{"name":2329,"slug":2330,"type":15},{"name":2332,"slug":2333,"type":15},{"name":9,"slug":8,"type":15},{"name":2336,"slug":2337,"type":15},61]