[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-posthog-planning-voice-agent-user-interviews":3,"mdc-8sqakg-key":46,"related-org-posthog-planning-voice-agent-user-interviews":2109,"related-repo-posthog-planning-voice-agent-user-interviews":2279},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":20,"repoUrl":21,"updatedAt":22,"license":23,"forks":24,"topics":25,"repo":41,"sourceUrl":44,"mdContent":45},"planning-voice-agent-user-interviews","plan user interviews with voice agents","Plan a round of user interviews conducted by PostHog's AI voice agent (a \"robo interviewer\") — the automated voice-agent interview product. Captures a UserInterviewTopic (who to target, what to ask, framing context, question list) and calls user-interview-topics-create. ONLY trigger when the user clearly wants an AI voice agent to actually run the interview calls (e.g. \"set up robo user interviews\", \"have the voice agent interview these users\"). Do NOT trigger for ordinary user research that does not involve the voice agent — finding or shortlisting users to talk to (\"who'd be a good fit to interview about Y\"), planning questions for a human-run interview, or analysing feedback are audience discovery, handled with normal data queries, not this skill. Also do NOT trigger for uploading a recorded interview audio file or browsing topics with user-interview-topics-list. When intent is ambiguous, first confirm what kind of research it is and whether they want an AI voice agent to conduct it (see Step 0).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"posthog","PostHog","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fposthog.png",[12,14,17],{"name":9,"slug":8,"type":13},"tag",{"name":15,"slug":16,"type":13},"User Research","user-research",{"name":18,"slug":19,"type":13},"Meetings","meetings",35568,"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog","2026-06-26T07:44:39.23216",null,2977,[26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"ab-testing","ai-analytics","analytics","cdp","data-warehouse","experiments","feature-flags","javascript","product-analytics","python","react","session-replay","surveys","typescript","web-analytics",{"repoUrl":21,"stars":20,"forks":24,"topics":42,"description":43},[26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"🦔 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\u002Fuser_interviews\u002Fskills\u002Fplanning-voice-agent-user-interviews","---\nname: planning-voice-agent-user-interviews\ndescription: 'Plan a round of user interviews conducted by PostHog''s AI voice agent (a \"robo interviewer\") — the automated voice-agent interview product. Captures a UserInterviewTopic (who to target, what to ask, framing context, question list) and calls user-interview-topics-create. ONLY trigger when the user clearly wants an AI voice agent to actually run the interview calls (e.g. \"set up robo user interviews\", \"have the voice agent interview these users\"). Do NOT trigger for ordinary user research that does not involve the voice agent — finding or shortlisting users to talk to (\"who''d be a good fit to interview about Y\"), planning questions for a human-run interview, or analysing feedback are audience discovery, handled with normal data queries, not this skill. Also do NOT trigger for uploading a recorded interview audio file or browsing topics with user-interview-topics-list. When intent is ambiguous, first confirm what kind of research it is and whether they want an AI voice agent to conduct it (see Step 0).'\n---\n\n# Planning voice-agent user interviews\n\nUse this skill **only** when someone wants PostHog's AI voice agent — a \"robo interviewer\" — to actually conduct a round of user interview calls for them. The plan is captured as a `UserInterviewTopic` that the voice agent later runs through, calling each targeted person and working through the questions.\n\nThis is a specific product, not a generic research helper. If the user only wants to _find_ or _shortlist_ people to interview, plan questions for an interview a human will run, or analyse feedback they already have, this is **not** the right skill — handle that as ordinary audience discovery \u002F data work (see `querying-posthog-data`) and, at most, mention that the voice-agent option exists.\n\n## Step 0: Confirm this is the voice-agent flow\n\nBefore doing anything else, make sure the user actually wants the AI voice agent to run the interviews. Many requests that mention \"interviewing users\" are really about discovering _who_ to talk to, not about handing the conversation to a robot.\n\n- If the user explicitly asked for the voice agent \u002F robo interviews \u002F automated calls, proceed to Step 1.\n- If they only asked to find or rank users to interview (e.g. \"who'd be a good fit to interview about the inbox?\"), treat it as audience discovery: answer it with normal queries and **do not** create a topic. You may add a one-line offer afterwards, e.g. _\"If you want, I can set these up as automated interviews run by PostHog's AI voice agent — want me to do that?\"_\n- If intent is ambiguous, ask first, e.g. _\"Quick check on what you're after: what kind of user research is this, and do you want PostHog's AI voice agent to actually run the interviews (it calls people and works through your questions)? Or did you just want me to find the right users to talk to?\"_\n\nOnly continue past this step once the user has confirmed they want the voice agent to conduct the interviews.\n\n## What a complete topic needs\n\nBefore calling `user-interview-topics-create`, gather these:\n\n1. **Who to interview** — at least one of:\n   - `interviewee_emails` — list of email addresses\n   - `interviewee_distinct_ids` — list of PostHog distinct IDs\n2. **What to ask about** — `topic` (required free text)\n3. **How the agent should frame the conversation** — optional `agent_context` (extra system prompt)\n4. **The questions to work through** — optional ordered `questions` list\n\nTopics snapshot their audience at create time — there is no live cohort link. If the user names a cohort, you (the agent) resolve cohort members to emails\u002Fdistinct_ids before calling `user-interview-topics-create`. See Step 2 for the resolution flow and the 500-member cap UX.\n\nThe API rejects topics with no targeting, so `interviewee_emails` and\u002For `interviewee_distinct_ids` must end up non-empty.\n\n## Step 1: Clarify intent\n\nIf the request is vague, ask:\n\n- **Which feature or behavior?** \"checkout\" might be the button click, the page view, or the payment submission — narrow it down to one event.\n- **What do you want to learn?** Why they bounced? What confused them? What alternatives they tried? The goal shapes both the audience and the questions.\n- **Which kind of users?** Heavy users (what works), drop-offs (what blocks adoption), at-risk users (what breaks retention), or a mix.\n\nSkip these questions only when the user has already answered them.\n\n## Step 2: Pick the audience\n\nMap what the user said to one of these paths:\n\n- **They named a cohort** (\"our power users\", \"trial signups last week\") — use `cohorts-list` (or a `system.cohorts` SQL search) to find the cohort, confirm the match, then resolve cohort members to emails\u002Fdistinct_ids (see \"Resolving a cohort\" below).\n- **They described the kind of person but no cohort exists** — offer to either create the cohort first (`cohorts-create`), then resolve it, or fall back to finding people by behavior (see below).\n- **They gave email addresses or distinct IDs** — accept them directly. Skip the cohort lookup.\n- **They described a behavior, not a cohort** (\"users who tried checkout but didn't finish\", \"people who used to use dark mode and stopped\") — find them by querying their events (see below).\n- **They were vague** (\"a few customers\", \"some power users\") — ask which they prefer:\n  - Pick an existing cohort → `cohorts-list`\n  - Look up specific persons by name or email → `persons-list` with a search query\n  - Find users by behavior → see below\n  - Paste a list of email addresses\n\nEach email passes through DRF email validation (display-name format `Paul D'Ambra \u003Cpaul@x.com>` is accepted alongside plain `paul@x.com`).\n\n### Resolving a cohort\n\nTopics snapshot their audience at create time. When the user picks a cohort, you must materialize the member list into `interviewee_emails` (and `interviewee_distinct_ids` for members without emails) before creating the topic.\n\n1. **Count the cohort first.** Cheap query, decides the next step:\n\n   ```sql\n   SELECT count() FROM persons WHERE id IN COHORT \u003Ccohort_id>\n   ```\n\n2. **If the cohort has 500 or fewer members**, fetch their emails:\n\n   ```sql\n   SELECT properties.email AS email\n   FROM persons\n   WHERE id IN COHORT \u003Ccohort_id> AND properties.email IS NOT NULL\n   LIMIT 500\n   ```\n\n   Put each row into `interviewee_emails`. Dedupe.\n\n   Cohort members without an email property aren't included by default — the `persons.id` column is the person UUID, not the SDK distinct_id, so it can't be used as an `interviewee_distinct_id` without a `pdi.distinct_id` join. If you specifically need to reach members who only exist as distinct IDs, ask the user first, then do the join explicitly.\n\n3. **If the cohort has more than 500 members**, stop and ask the user. Do not silently truncate, sample, or fall back to a different cohort — the user needs to choose. Surface:\n   - The cohort name and count (e.g. \"PostHog Team has 28,563 members — over the 500 cap.\")\n   - Why the cap exists (\"we snapshot the audience at create time, and 500 is an agent-side guardrail to keep one interview campaign manageable — the backend itself does not cap the array length\")\n   - Their options:\n     - **Narrow the cohort** — describe the subset they actually want (e.g. \"engineers only\", \"active in the last 30 days\"). You can offer to write a more specific HogQL filter or create a new, smaller cohort via `cohorts-create`.\n     - **Sample randomly** — confirm a count (e.g. 200) and use `ORDER BY rand() LIMIT \u003Cn>` on the cohort query. Make the randomness explicit so they know they're not getting the \"top\" members.\n     - **Paste a curated list** — they take over and provide emails directly.\n\n   Pick the path with them, then re-run the resolution. Never proceed without an explicit decision.\n\n4. **Tell the user what you resolved.** After resolution, confirm before creating: \"Cohort 'X' has N members, resolved to E emails and D distinct IDs (snapshot — won't update if the cohort changes later).\" This makes the snapshot semantics visible.\n\n### Finding users by behavior\n\nWhen the user describes who they want to talk to in behavioral terms, find them in the project's own data:\n\n1. **Find the right event.** Call `read-data-schema` to list events that actually exist in the project. Don't guess event names from training data — PostHog event taxonomies are bespoke. Match the user's description to one or two candidate events; if multiple plausible matches exist, list them and ask which behavior they care about.\n2. **Query for users.** Call `execute-sql` with HogQL. Filter by the chosen event over the last 60 days and group per person with `coalesce(person.properties.email, distinct_id) AS id` — this keeps both kinds of rows in a single query: people with an email group under it (directly usable as `interviewee_emails`), while emailless people fall back to their own `distinct_id` (for `interviewee_distinct_ids`) instead of collapsing into one junk `None` bucket that would sort to the top under `ORDER BY count() DESC` and eat a slot of the sample. The selected `email` column tells you which is which when routing (see below). The aggregates in each template (`event_count`, `last_seen`, `days_since_last_seen`) are what feed Step 5's per-interviewee context. Replace `\u003Cevent_name>` with the chosen event:\n   - **Heavy users** — `SELECT coalesce(person.properties.email, distinct_id) AS id, person.properties.email AS email, count() AS event_count, max(timestamp) AS last_seen, dateDiff('day', max(timestamp), now()) AS days_since_last_seen FROM events WHERE event = '\u003Cevent_name>' AND timestamp > now() - INTERVAL 60 DAY GROUP BY id, email HAVING count() >= 5 ORDER BY count() DESC LIMIT 20`\n   - **Drop-offs** (tried once or twice and never came back) — `SELECT coalesce(person.properties.email, distinct_id) AS id, person.properties.email AS email, count() AS event_count, max(timestamp) AS last_seen, dateDiff('day', max(timestamp), now()) AS days_since_last_seen FROM events WHERE event = '\u003Cevent_name>' AND timestamp > now() - INTERVAL 60 DAY GROUP BY id, email HAVING count() \u003C= 2 AND dateDiff('day', max(timestamp), now()) > 14 ORDER BY count() ASC LIMIT 20`\n   - **At-risk** (was active, now dormant) — `SELECT coalesce(person.properties.email, distinct_id) AS id, person.properties.email AS email, count() AS event_count, max(timestamp) AS last_seen, dateDiff('day', max(timestamp), now()) AS days_since_last_seen FROM events WHERE event = '\u003Cevent_name>' AND timestamp > now() - INTERVAL 60 DAY GROUP BY id, email HAVING count() >= 3 AND dateDiff('day', max(timestamp), now()) > 14 ORDER BY days_since_last_seen DESC LIMIT 20`\n3. **Build a balanced sample.** Unless the user asked for one specific segment, mixing 5 heavy users + 3 drop-offs + 2 at-risk users yields the most actionable interviews: you learn what works, what blocks adoption, and what breaks retention. Adjust counts to match what the user actually wants.\n\nRoute each row by its `email` column: rows where `email` is non-null go into `interviewee_emails`, and rows where `email` is null (so `id` holds the `distinct_id`) go into `interviewee_distinct_ids` — both can be set on the same topic. Keep `event_count` and `days_since_last_seen` per person so Step 5 can synthesise context like \"used checkout 47 times in last 60 days; last seen 2 days ago\".\n\n## Step 3: Capture the topic\n\n`topic` is one or two sentences describing what the interview is about. Infer from context where possible — don't ask the user to repeat themselves.\n\nExample: \"ask trial users why they didn't convert\" → `topic: \"Why trial users didn't convert in the first 14 days\"`.\n\n## Step 4: Prepare the voice agent\n\nTwo fields shape what the agent actually does on the call. **Always ask about both before creating the topic.**\n\n### Always ask: what questions do they want to ask?\n\n`questions` is an ordered list the agent works through. Anchors, not a script — the agent will adapt phrasing. Keep them open-ended:\n\n- ✅ \"What made you decide to try PostHog?\"\n- ❌ \"Did you like PostHog?\"\n\nIf the user already listed questions in their original request, use those and confirm. Otherwise, ask explicitly: _\"What questions do you want the agent to ask?\"_\n\nIf the user can't think of any, suggest 3–5 open-ended questions drawn from the `topic` and offer them for review before creating.\n\nThe field is technically optional in the API, but don't skip it silently — an interview with no questions is rarely useful.\n\nQuestion templates by research goal:\n\n- **Why users dropped off \u002F churned**:\n  - \"Tell me about the last time you tried [feature] — what were you trying to do?\"\n  - \"Walk me through what happened step-by-step.\"\n  - \"What did you expect vs what actually happened?\"\n  - \"What made you stop or decide not to continue?\"\n  - \"What would need to change for you to use [feature] regularly?\"\n- **Why heavy users love a feature**:\n  - \"Tell me about how you use [feature] — what problem does it solve for you?\"\n  - \"Walk me through your typical workflow.\"\n  - \"What would you do if [feature] didn't exist?\"\n  - \"What almost made you not use it when you first tried?\"\n  - \"What's one thing you wish it did differently?\"\n- **Why someone hasn't tried a feature yet**:\n  - \"Have you noticed [feature] in the product?\"\n  - \"What's stopped you from trying it?\"\n  - \"What would have to be true for it to be worth trying?\"\n\n### Always offer: extra context to guide the interview\n\n`agent_context` is optional, but a few sentences here make the conversation dramatically better. Always offer the user the chance to provide it, e.g.:\n\n> _\"Want to give the agent any extra context? Things like tone, what to avoid, or background on the interviewee help guide the conversation. It's optional.\"_\n\nUseful kinds of context:\n\n- **Tone**: \"warm and conversational\", \"skip pleasantries — this is a 10-minute call\"\n- **Constraints**: \"don't promise feature delivery\", \"do not discuss pricing\"\n- **Background the agent should know**: \"the user just churned from the Scale plan; be empathetic\", \"this person tried PostHog 6 months ago and bounced\"\n- **Persona**: \"you are Sam, a PostHog product researcher\"\n\nIf the user declines, that's fine — leave `agent_context` empty and continue.\n\n## Calling user-interview-topics-create\n\nOnce you have the pieces:\n\n```json\n{\n  \"topic\": \"Why trial users churned in week 2\",\n  \"interviewee_emails\": [\"paul@acme.com\", \"alex@beta.com\"],\n  \"interviewee_distinct_ids\": [\"distinct-id-with-no-email\"],\n  \"agent_context\": \"Be warm. The interviewee just churned — don't pitch.\",\n  \"questions\": [\n    \"What were you hoping PostHog would help with?\",\n    \"Where did you get stuck?\",\n    \"What would have made you stay?\"\n  ]\n}\n```\n\nAfter creation, capture the returned topic ID — you'll need it for Step 5 and for handing off to the voice agent.\n\n## Step 5: Optionally attach per-interviewee context\n\nThe topic-level `agent_context` applies to every interviewee. If the user knows something specific about individual interviewees that should shape that one conversation, attach it as a per-interviewee row via `user-interview-topics-interviewees-create`. This is optional — most topics won't need it.\n\nEach row pairs an `interviewee_identifier` (must match one of the emails or distinct IDs in the parent topic's targeting) with an `agent_context` string. At most one row per (topic, interviewee). A user can have zero rows.\n\nGood per-interviewee context looks like:\n\n- \"uses the replay product but has never used summarization\"\n- \"churned from Scale plan last month — be empathetic, don't pitch\"\n- \"founder, very technical, skip basic product explanations\"\n\nAfter Step 4 succeeds, ask the user: _\"Want to add per-interviewee context? Useful when individual people have very different backgrounds. You can either dictate the rows or paste a CSV.\"_\n\nIf you found the audience via behavioral query in Step 2, you already have per-person context (usage counts, dormancy windows). Use it: e.g. `\"used checkout 47 times in last 60 days; last seen 2 days ago\"` for heavy users, `\"tried checkout once 18 days ago, never returned\"` for drop-offs.\n\n### Accepting CSV input\n\nIf the user pastes a CSV, expect two columns: `identifier,context`. Either with or without a header row. Examples:\n\n```csv\npaul@acme.com,uses replay but never summarization\nsteve@apple.com,founder; very technical; skip product basics\n```\n\nOr with a header:\n\n```csv\nidentifier,context\nabc-distinct-id-1,churned from Scale last month — be empathetic\n```\n\nParse the CSV, then create the rows with the captured `topic_id`. For more than a couple of rows, prefer `user-interview-topics-interviewees-bulk-create` — it takes an `items` array of `(interviewee_identifier, agent_context)` and creates up to 500 rows in one request, reporting `inserted_count`, `skipped_count`, and `skipped_identifiers` for pairs that already exist. Fall back to `user-interview-topics-interviewees-create` for a single row. Skip blank lines. Quote-escape commas inside the context cell — standard CSV rules.\n\nIf a row's identifier isn't present in the parent topic's `interviewee_emails` or `interviewee_distinct_ids`, warn the user before creating — the voice agent looks up context by exact string match, so a mismatched identifier just gets ignored at runtime.\n\n## Edge cases\n\n- **No users match the behavioral query.** Possible reasons: the event isn't firing, the date range is too narrow, or no users have email addresses captured as person properties. Offer to widen the date range, try a different event, or fall back to cohorts \u002F explicit emails.\n- **Users matched but few have emails.** PostHog stores whatever the SDK captures. If only a handful of matching users have email addresses on their person profile, surface the count and ask: take the smaller sample, fall back to `interviewee_distinct_ids` (the agent can still reach them via in-app delivery), or skip the behavioral query and let the user paste emails directly.\n- **Ambiguous event name.** If `read-data-schema` returns multiple candidates (e.g. `checkout_started`, `checkout_completed`, `checkout_abandoned`), list them with counts and let the user pick the behavior they want to understand. Don't pick silently.\n- **User asks to interview only drop-offs (or only one segment).** That works, but flag the tradeoff: interviewing only drop-offs tells you what's broken without telling you what works. Recommend including 2–3 successful users for contrast unless the user has a reason for the narrower sample.\n\n## What this skill is not for\n\n- **Uploading a recorded interview** — that's the separate `UserInterview` model (`user_interviews_create` with an audio file). Different flow, different model.\n- **Listing existing topics** — `user-interview-topics-list` handles that directly with `search`, `limit`, and `offset`. No skill needed.\n- **Analyzing transcripts after the interview** — out of scope here; that lives with the recorded `UserInterview` flow.\n",{"data":47,"body":48},{"name":4,"description":6},{"type":49,"children":50},"root",[51,59,82,117,124,136,173,178,184,197,290,302,321,327,332,365,370,376,381,494,515,522,541,760,766,771,954,1023,1029,1039,1051,1057,1067,1073,1083,1096,1106,1118,1123,1128,1264,1270,1280,1292,1297,1340,1352,1358,1363,1665,1670,1676,1696,1716,1721,1739,1749,1770,1776,1789,1814,1819,1842,1909,1927,1933,2012,2018,2103],{"type":52,"tag":53,"props":54,"children":55},"element","h1",{"id":4},[56],{"type":57,"value":58},"text","Planning voice-agent user interviews",{"type":52,"tag":60,"props":61,"children":62},"p",{},[63,65,71,73,80],{"type":57,"value":64},"Use this skill ",{"type":52,"tag":66,"props":67,"children":68},"strong",{},[69],{"type":57,"value":70},"only",{"type":57,"value":72}," when someone wants PostHog's AI voice agent — a \"robo interviewer\" — to actually conduct a round of user interview calls for them. The plan is captured as a ",{"type":52,"tag":74,"props":75,"children":77},"code",{"className":76},[],[78],{"type":57,"value":79},"UserInterviewTopic",{"type":57,"value":81}," that the voice agent later runs through, calling each targeted person and working through the questions.",{"type":52,"tag":60,"props":83,"children":84},{},[85,87,93,95,100,102,107,109,115],{"type":57,"value":86},"This is a specific product, not a generic research helper. If the user only wants to ",{"type":52,"tag":88,"props":89,"children":90},"em",{},[91],{"type":57,"value":92},"find",{"type":57,"value":94}," or ",{"type":52,"tag":88,"props":96,"children":97},{},[98],{"type":57,"value":99},"shortlist",{"type":57,"value":101}," people to interview, plan questions for an interview a human will run, or analyse feedback they already have, this is ",{"type":52,"tag":66,"props":103,"children":104},{},[105],{"type":57,"value":106},"not",{"type":57,"value":108}," the right skill — handle that as ordinary audience discovery \u002F data work (see ",{"type":52,"tag":74,"props":110,"children":112},{"className":111},[],[113],{"type":57,"value":114},"querying-posthog-data",{"type":57,"value":116},") and, at most, mention that the voice-agent option exists.",{"type":52,"tag":118,"props":119,"children":121},"h2",{"id":120},"step-0-confirm-this-is-the-voice-agent-flow",[122],{"type":57,"value":123},"Step 0: Confirm this is the voice-agent flow",{"type":52,"tag":60,"props":125,"children":126},{},[127,129,134],{"type":57,"value":128},"Before doing anything else, make sure the user actually wants the AI voice agent to run the interviews. Many requests that mention \"interviewing users\" are really about discovering ",{"type":52,"tag":88,"props":130,"children":131},{},[132],{"type":57,"value":133},"who",{"type":57,"value":135}," to talk to, not about handing the conversation to a robot.",{"type":52,"tag":137,"props":138,"children":139},"ul",{},[140,146,163],{"type":52,"tag":141,"props":142,"children":143},"li",{},[144],{"type":57,"value":145},"If the user explicitly asked for the voice agent \u002F robo interviews \u002F automated calls, proceed to Step 1.",{"type":52,"tag":141,"props":147,"children":148},{},[149,151,156,158],{"type":57,"value":150},"If they only asked to find or rank users to interview (e.g. \"who'd be a good fit to interview about the inbox?\"), treat it as audience discovery: answer it with normal queries and ",{"type":52,"tag":66,"props":152,"children":153},{},[154],{"type":57,"value":155},"do not",{"type":57,"value":157}," create a topic. You may add a one-line offer afterwards, e.g. ",{"type":52,"tag":88,"props":159,"children":160},{},[161],{"type":57,"value":162},"\"If you want, I can set these up as automated interviews run by PostHog's AI voice agent — want me to do that?\"",{"type":52,"tag":141,"props":164,"children":165},{},[166,168],{"type":57,"value":167},"If intent is ambiguous, ask first, e.g. ",{"type":52,"tag":88,"props":169,"children":170},{},[171],{"type":57,"value":172},"\"Quick check on what you're after: what kind of user research is this, and do you want PostHog's AI voice agent to actually run the interviews (it calls people and works through your questions)? Or did you just want me to find the right users to talk to?\"",{"type":52,"tag":60,"props":174,"children":175},{},[176],{"type":57,"value":177},"Only continue past this step once the user has confirmed they want the voice agent to conduct the interviews.",{"type":52,"tag":118,"props":179,"children":181},{"id":180},"what-a-complete-topic-needs",[182],{"type":57,"value":183},"What a complete topic needs",{"type":52,"tag":60,"props":185,"children":186},{},[187,189,195],{"type":57,"value":188},"Before calling ",{"type":52,"tag":74,"props":190,"children":192},{"className":191},[],[193],{"type":57,"value":194},"user-interview-topics-create",{"type":57,"value":196},", gather these:",{"type":52,"tag":198,"props":199,"children":200},"ol",{},[201,236,254,272],{"type":52,"tag":141,"props":202,"children":203},{},[204,209,211],{"type":52,"tag":66,"props":205,"children":206},{},[207],{"type":57,"value":208},"Who to interview",{"type":57,"value":210}," — at least one of:\n",{"type":52,"tag":137,"props":212,"children":213},{},[214,225],{"type":52,"tag":141,"props":215,"children":216},{},[217,223],{"type":52,"tag":74,"props":218,"children":220},{"className":219},[],[221],{"type":57,"value":222},"interviewee_emails",{"type":57,"value":224}," — list of email addresses",{"type":52,"tag":141,"props":226,"children":227},{},[228,234],{"type":52,"tag":74,"props":229,"children":231},{"className":230},[],[232],{"type":57,"value":233},"interviewee_distinct_ids",{"type":57,"value":235}," — list of PostHog distinct IDs",{"type":52,"tag":141,"props":237,"children":238},{},[239,244,246,252],{"type":52,"tag":66,"props":240,"children":241},{},[242],{"type":57,"value":243},"What to ask about",{"type":57,"value":245}," — ",{"type":52,"tag":74,"props":247,"children":249},{"className":248},[],[250],{"type":57,"value":251},"topic",{"type":57,"value":253}," (required free text)",{"type":52,"tag":141,"props":255,"children":256},{},[257,262,264,270],{"type":52,"tag":66,"props":258,"children":259},{},[260],{"type":57,"value":261},"How the agent should frame the conversation",{"type":57,"value":263}," — optional ",{"type":52,"tag":74,"props":265,"children":267},{"className":266},[],[268],{"type":57,"value":269},"agent_context",{"type":57,"value":271}," (extra system prompt)",{"type":52,"tag":141,"props":273,"children":274},{},[275,280,282,288],{"type":52,"tag":66,"props":276,"children":277},{},[278],{"type":57,"value":279},"The questions to work through",{"type":57,"value":281}," — optional ordered ",{"type":52,"tag":74,"props":283,"children":285},{"className":284},[],[286],{"type":57,"value":287},"questions",{"type":57,"value":289}," list",{"type":52,"tag":60,"props":291,"children":292},{},[293,295,300],{"type":57,"value":294},"Topics snapshot their audience at create time — there is no live cohort link. If the user names a cohort, you (the agent) resolve cohort members to emails\u002Fdistinct_ids before calling ",{"type":52,"tag":74,"props":296,"children":298},{"className":297},[],[299],{"type":57,"value":194},{"type":57,"value":301},". See Step 2 for the resolution flow and the 500-member cap UX.",{"type":52,"tag":60,"props":303,"children":304},{},[305,307,312,314,319],{"type":57,"value":306},"The API rejects topics with no targeting, so ",{"type":52,"tag":74,"props":308,"children":310},{"className":309},[],[311],{"type":57,"value":222},{"type":57,"value":313}," and\u002For ",{"type":52,"tag":74,"props":315,"children":317},{"className":316},[],[318],{"type":57,"value":233},{"type":57,"value":320}," must end up non-empty.",{"type":52,"tag":118,"props":322,"children":324},{"id":323},"step-1-clarify-intent",[325],{"type":57,"value":326},"Step 1: Clarify intent",{"type":52,"tag":60,"props":328,"children":329},{},[330],{"type":57,"value":331},"If the request is vague, ask:",{"type":52,"tag":137,"props":333,"children":334},{},[335,345,355],{"type":52,"tag":141,"props":336,"children":337},{},[338,343],{"type":52,"tag":66,"props":339,"children":340},{},[341],{"type":57,"value":342},"Which feature or behavior?",{"type":57,"value":344}," \"checkout\" might be the button click, the page view, or the payment submission — narrow it down to one event.",{"type":52,"tag":141,"props":346,"children":347},{},[348,353],{"type":52,"tag":66,"props":349,"children":350},{},[351],{"type":57,"value":352},"What do you want to learn?",{"type":57,"value":354}," Why they bounced? What confused them? What alternatives they tried? The goal shapes both the audience and the questions.",{"type":52,"tag":141,"props":356,"children":357},{},[358,363],{"type":52,"tag":66,"props":359,"children":360},{},[361],{"type":57,"value":362},"Which kind of users?",{"type":57,"value":364}," Heavy users (what works), drop-offs (what blocks adoption), at-risk users (what breaks retention), or a mix.",{"type":52,"tag":60,"props":366,"children":367},{},[368],{"type":57,"value":369},"Skip these questions only when the user has already answered them.",{"type":52,"tag":118,"props":371,"children":373},{"id":372},"step-2-pick-the-audience",[374],{"type":57,"value":375},"Step 2: Pick the audience",{"type":52,"tag":60,"props":377,"children":378},{},[379],{"type":57,"value":380},"Map what the user said to one of these paths:",{"type":52,"tag":137,"props":382,"children":383},{},[384,410,428,438,448],{"type":52,"tag":141,"props":385,"children":386},{},[387,392,394,400,402,408],{"type":52,"tag":66,"props":388,"children":389},{},[390],{"type":57,"value":391},"They named a cohort",{"type":57,"value":393}," (\"our power users\", \"trial signups last week\") — use ",{"type":52,"tag":74,"props":395,"children":397},{"className":396},[],[398],{"type":57,"value":399},"cohorts-list",{"type":57,"value":401}," (or a ",{"type":52,"tag":74,"props":403,"children":405},{"className":404},[],[406],{"type":57,"value":407},"system.cohorts",{"type":57,"value":409}," SQL search) to find the cohort, confirm the match, then resolve cohort members to emails\u002Fdistinct_ids (see \"Resolving a cohort\" below).",{"type":52,"tag":141,"props":411,"children":412},{},[413,418,420,426],{"type":52,"tag":66,"props":414,"children":415},{},[416],{"type":57,"value":417},"They described the kind of person but no cohort exists",{"type":57,"value":419}," — offer to either create the cohort first (",{"type":52,"tag":74,"props":421,"children":423},{"className":422},[],[424],{"type":57,"value":425},"cohorts-create",{"type":57,"value":427},"), then resolve it, or fall back to finding people by behavior (see below).",{"type":52,"tag":141,"props":429,"children":430},{},[431,436],{"type":52,"tag":66,"props":432,"children":433},{},[434],{"type":57,"value":435},"They gave email addresses or distinct IDs",{"type":57,"value":437}," — accept them directly. Skip the cohort lookup.",{"type":52,"tag":141,"props":439,"children":440},{},[441,446],{"type":52,"tag":66,"props":442,"children":443},{},[444],{"type":57,"value":445},"They described a behavior, not a cohort",{"type":57,"value":447}," (\"users who tried checkout but didn't finish\", \"people who used to use dark mode and stopped\") — find them by querying their events (see below).",{"type":52,"tag":141,"props":449,"children":450},{},[451,456,458],{"type":52,"tag":66,"props":452,"children":453},{},[454],{"type":57,"value":455},"They were vague",{"type":57,"value":457}," (\"a few customers\", \"some power users\") — ask which they prefer:\n",{"type":52,"tag":137,"props":459,"children":460},{},[461,471,484,489],{"type":52,"tag":141,"props":462,"children":463},{},[464,466],{"type":57,"value":465},"Pick an existing cohort → ",{"type":52,"tag":74,"props":467,"children":469},{"className":468},[],[470],{"type":57,"value":399},{"type":52,"tag":141,"props":472,"children":473},{},[474,476,482],{"type":57,"value":475},"Look up specific persons by name or email → ",{"type":52,"tag":74,"props":477,"children":479},{"className":478},[],[480],{"type":57,"value":481},"persons-list",{"type":57,"value":483}," with a search query",{"type":52,"tag":141,"props":485,"children":486},{},[487],{"type":57,"value":488},"Find users by behavior → see below",{"type":52,"tag":141,"props":490,"children":491},{},[492],{"type":57,"value":493},"Paste a list of email addresses",{"type":52,"tag":60,"props":495,"children":496},{},[497,499,505,507,513],{"type":57,"value":498},"Each email passes through DRF email validation (display-name format ",{"type":52,"tag":74,"props":500,"children":502},{"className":501},[],[503],{"type":57,"value":504},"Paul D'Ambra \u003Cpaul@x.com>",{"type":57,"value":506}," is accepted alongside plain ",{"type":52,"tag":74,"props":508,"children":510},{"className":509},[],[511],{"type":57,"value":512},"paul@x.com",{"type":57,"value":514},").",{"type":52,"tag":516,"props":517,"children":519},"h3",{"id":518},"resolving-a-cohort",[520],{"type":57,"value":521},"Resolving a cohort",{"type":52,"tag":60,"props":523,"children":524},{},[525,527,532,534,539],{"type":57,"value":526},"Topics snapshot their audience at create time. When the user picks a cohort, you must materialize the member list into ",{"type":52,"tag":74,"props":528,"children":530},{"className":529},[],[531],{"type":57,"value":222},{"type":57,"value":533}," (and ",{"type":52,"tag":74,"props":535,"children":537},{"className":536},[],[538],{"type":57,"value":233},{"type":57,"value":540}," for members without emails) before creating the topic.",{"type":52,"tag":198,"props":542,"children":543},{},[544,575,669,750],{"type":52,"tag":141,"props":545,"children":546},{},[547,552,554],{"type":52,"tag":66,"props":548,"children":549},{},[550],{"type":57,"value":551},"Count the cohort first.",{"type":57,"value":553}," Cheap query, decides the next step:",{"type":52,"tag":555,"props":556,"children":561},"pre",{"className":557,"code":558,"language":559,"meta":560,"style":560},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","SELECT count() FROM persons WHERE id IN COHORT \u003Ccohort_id>\n","sql","",[562],{"type":52,"tag":74,"props":563,"children":564},{"__ignoreMap":560},[565],{"type":52,"tag":566,"props":567,"children":570},"span",{"class":568,"line":569},"line",1,[571],{"type":52,"tag":566,"props":572,"children":573},{},[574],{"type":57,"value":558},{"type":52,"tag":141,"props":576,"children":577},{},[578,583,585,627,631,633,638,640,643,645,651,653,659,661,667],{"type":52,"tag":66,"props":579,"children":580},{},[581],{"type":57,"value":582},"If the cohort has 500 or fewer members",{"type":57,"value":584},", fetch their emails:",{"type":52,"tag":555,"props":586,"children":588},{"className":557,"code":587,"language":559,"meta":560,"style":560},"SELECT properties.email AS email\nFROM persons\nWHERE id IN COHORT \u003Ccohort_id> AND properties.email IS NOT NULL\nLIMIT 500\n",[589],{"type":52,"tag":74,"props":590,"children":591},{"__ignoreMap":560},[592,600,609,618],{"type":52,"tag":566,"props":593,"children":594},{"class":568,"line":569},[595],{"type":52,"tag":566,"props":596,"children":597},{},[598],{"type":57,"value":599},"SELECT properties.email AS email\n",{"type":52,"tag":566,"props":601,"children":603},{"class":568,"line":602},2,[604],{"type":52,"tag":566,"props":605,"children":606},{},[607],{"type":57,"value":608},"FROM persons\n",{"type":52,"tag":566,"props":610,"children":612},{"class":568,"line":611},3,[613],{"type":52,"tag":566,"props":614,"children":615},{},[616],{"type":57,"value":617},"WHERE id IN COHORT \u003Ccohort_id> AND properties.email IS NOT NULL\n",{"type":52,"tag":566,"props":619,"children":621},{"class":568,"line":620},4,[622],{"type":52,"tag":566,"props":623,"children":624},{},[625],{"type":57,"value":626},"LIMIT 500\n",{"type":52,"tag":628,"props":629,"children":630},"br",{},[],{"type":57,"value":632},"Put each row into ",{"type":52,"tag":74,"props":634,"children":636},{"className":635},[],[637],{"type":57,"value":222},{"type":57,"value":639},". Dedupe.",{"type":52,"tag":628,"props":641,"children":642},{},[],{"type":57,"value":644},"Cohort members without an email property aren't included by default — the ",{"type":52,"tag":74,"props":646,"children":648},{"className":647},[],[649],{"type":57,"value":650},"persons.id",{"type":57,"value":652}," column is the person UUID, not the SDK distinct_id, so it can't be used as an ",{"type":52,"tag":74,"props":654,"children":656},{"className":655},[],[657],{"type":57,"value":658},"interviewee_distinct_id",{"type":57,"value":660}," without a ",{"type":52,"tag":74,"props":662,"children":664},{"className":663},[],[665],{"type":57,"value":666},"pdi.distinct_id",{"type":57,"value":668}," join. If you specifically need to reach members who only exist as distinct IDs, ask the user first, then do the join explicitly.",{"type":52,"tag":141,"props":670,"children":671},{},[672,677,679,745,748],{"type":52,"tag":66,"props":673,"children":674},{},[675],{"type":57,"value":676},"If the cohort has more than 500 members",{"type":57,"value":678},", stop and ask the user. Do not silently truncate, sample, or fall back to a different cohort — the user needs to choose. Surface:",{"type":52,"tag":137,"props":680,"children":681},{},[682,687,692],{"type":52,"tag":141,"props":683,"children":684},{},[685],{"type":57,"value":686},"The cohort name and count (e.g. \"PostHog Team has 28,563 members — over the 500 cap.\")",{"type":52,"tag":141,"props":688,"children":689},{},[690],{"type":57,"value":691},"Why the cap exists (\"we snapshot the audience at create time, and 500 is an agent-side guardrail to keep one interview campaign manageable — the backend itself does not cap the array length\")",{"type":52,"tag":141,"props":693,"children":694},{},[695,697],{"type":57,"value":696},"Their options:\n",{"type":52,"tag":137,"props":698,"children":699},{},[700,717,735],{"type":52,"tag":141,"props":701,"children":702},{},[703,708,710,715],{"type":52,"tag":66,"props":704,"children":705},{},[706],{"type":57,"value":707},"Narrow the cohort",{"type":57,"value":709}," — describe the subset they actually want (e.g. \"engineers only\", \"active in the last 30 days\"). You can offer to write a more specific HogQL filter or create a new, smaller cohort via ",{"type":52,"tag":74,"props":711,"children":713},{"className":712},[],[714],{"type":57,"value":425},{"type":57,"value":716},".",{"type":52,"tag":141,"props":718,"children":719},{},[720,725,727,733],{"type":52,"tag":66,"props":721,"children":722},{},[723],{"type":57,"value":724},"Sample randomly",{"type":57,"value":726}," — confirm a count (e.g. 200) and use ",{"type":52,"tag":74,"props":728,"children":730},{"className":729},[],[731],{"type":57,"value":732},"ORDER BY rand() LIMIT \u003Cn>",{"type":57,"value":734}," on the cohort query. Make the randomness explicit so they know they're not getting the \"top\" members.",{"type":52,"tag":141,"props":736,"children":737},{},[738,743],{"type":52,"tag":66,"props":739,"children":740},{},[741],{"type":57,"value":742},"Paste a curated list",{"type":57,"value":744}," — they take over and provide emails directly.",{"type":52,"tag":628,"props":746,"children":747},{},[],{"type":57,"value":749},"Pick the path with them, then re-run the resolution. Never proceed without an explicit decision.",{"type":52,"tag":141,"props":751,"children":752},{},[753,758],{"type":52,"tag":66,"props":754,"children":755},{},[756],{"type":57,"value":757},"Tell the user what you resolved.",{"type":57,"value":759}," After resolution, confirm before creating: \"Cohort 'X' has N members, resolved to E emails and D distinct IDs (snapshot — won't update if the cohort changes later).\" This makes the snapshot semantics visible.",{"type":52,"tag":516,"props":761,"children":763},{"id":762},"finding-users-by-behavior",[764],{"type":57,"value":765},"Finding users by behavior",{"type":52,"tag":60,"props":767,"children":768},{},[769],{"type":57,"value":770},"When the user describes who they want to talk to in behavioral terms, find them in the project's own data:",{"type":52,"tag":198,"props":772,"children":773},{},[774,792,944],{"type":52,"tag":141,"props":775,"children":776},{},[777,782,784,790],{"type":52,"tag":66,"props":778,"children":779},{},[780],{"type":57,"value":781},"Find the right event.",{"type":57,"value":783}," Call ",{"type":52,"tag":74,"props":785,"children":787},{"className":786},[],[788],{"type":57,"value":789},"read-data-schema",{"type":57,"value":791}," to list events that actually exist in the project. Don't guess event names from training data — PostHog event taxonomies are bespoke. Match the user's description to one or two candidate events; if multiple plausible matches exist, list them and ask which behavior they care about.",{"type":52,"tag":141,"props":793,"children":794},{},[795,800,801,807,809,815,817,822,824,830,832,837,839,845,847,853,855,861,863,869,871,877,878,884,886,892,894],{"type":52,"tag":66,"props":796,"children":797},{},[798],{"type":57,"value":799},"Query for users.",{"type":57,"value":783},{"type":52,"tag":74,"props":802,"children":804},{"className":803},[],[805],{"type":57,"value":806},"execute-sql",{"type":57,"value":808}," with HogQL. Filter by the chosen event over the last 60 days and group per person with ",{"type":52,"tag":74,"props":810,"children":812},{"className":811},[],[813],{"type":57,"value":814},"coalesce(person.properties.email, distinct_id) AS id",{"type":57,"value":816}," — this keeps both kinds of rows in a single query: people with an email group under it (directly usable as ",{"type":52,"tag":74,"props":818,"children":820},{"className":819},[],[821],{"type":57,"value":222},{"type":57,"value":823},"), while emailless people fall back to their own ",{"type":52,"tag":74,"props":825,"children":827},{"className":826},[],[828],{"type":57,"value":829},"distinct_id",{"type":57,"value":831}," (for ",{"type":52,"tag":74,"props":833,"children":835},{"className":834},[],[836],{"type":57,"value":233},{"type":57,"value":838},") instead of collapsing into one junk ",{"type":52,"tag":74,"props":840,"children":842},{"className":841},[],[843],{"type":57,"value":844},"None",{"type":57,"value":846}," bucket that would sort to the top under ",{"type":52,"tag":74,"props":848,"children":850},{"className":849},[],[851],{"type":57,"value":852},"ORDER BY count() DESC",{"type":57,"value":854}," and eat a slot of the sample. The selected ",{"type":52,"tag":74,"props":856,"children":858},{"className":857},[],[859],{"type":57,"value":860},"email",{"type":57,"value":862}," column tells you which is which when routing (see below). The aggregates in each template (",{"type":52,"tag":74,"props":864,"children":866},{"className":865},[],[867],{"type":57,"value":868},"event_count",{"type":57,"value":870},", ",{"type":52,"tag":74,"props":872,"children":874},{"className":873},[],[875],{"type":57,"value":876},"last_seen",{"type":57,"value":870},{"type":52,"tag":74,"props":879,"children":881},{"className":880},[],[882],{"type":57,"value":883},"days_since_last_seen",{"type":57,"value":885},") are what feed Step 5's per-interviewee context. Replace ",{"type":52,"tag":74,"props":887,"children":889},{"className":888},[],[890],{"type":57,"value":891},"\u003Cevent_name>",{"type":57,"value":893}," with the chosen event:\n",{"type":52,"tag":137,"props":895,"children":896},{},[897,912,928],{"type":52,"tag":141,"props":898,"children":899},{},[900,905,906],{"type":52,"tag":66,"props":901,"children":902},{},[903],{"type":57,"value":904},"Heavy users",{"type":57,"value":245},{"type":52,"tag":74,"props":907,"children":909},{"className":908},[],[910],{"type":57,"value":911},"SELECT coalesce(person.properties.email, distinct_id) AS id, person.properties.email AS email, count() AS event_count, max(timestamp) AS last_seen, dateDiff('day', max(timestamp), now()) AS days_since_last_seen FROM events WHERE event = '\u003Cevent_name>' AND timestamp > now() - INTERVAL 60 DAY GROUP BY id, email HAVING count() >= 5 ORDER BY count() DESC LIMIT 20",{"type":52,"tag":141,"props":913,"children":914},{},[915,920,922],{"type":52,"tag":66,"props":916,"children":917},{},[918],{"type":57,"value":919},"Drop-offs",{"type":57,"value":921}," (tried once or twice and never came back) — ",{"type":52,"tag":74,"props":923,"children":925},{"className":924},[],[926],{"type":57,"value":927},"SELECT coalesce(person.properties.email, distinct_id) AS id, person.properties.email AS email, count() AS event_count, max(timestamp) AS last_seen, dateDiff('day', max(timestamp), now()) AS days_since_last_seen FROM events WHERE event = '\u003Cevent_name>' AND timestamp > now() - INTERVAL 60 DAY GROUP BY id, email HAVING count() \u003C= 2 AND dateDiff('day', max(timestamp), now()) > 14 ORDER BY count() ASC LIMIT 20",{"type":52,"tag":141,"props":929,"children":930},{},[931,936,938],{"type":52,"tag":66,"props":932,"children":933},{},[934],{"type":57,"value":935},"At-risk",{"type":57,"value":937}," (was active, now dormant) — ",{"type":52,"tag":74,"props":939,"children":941},{"className":940},[],[942],{"type":57,"value":943},"SELECT coalesce(person.properties.email, distinct_id) AS id, person.properties.email AS email, count() AS event_count, max(timestamp) AS last_seen, dateDiff('day', max(timestamp), now()) AS days_since_last_seen FROM events WHERE event = '\u003Cevent_name>' AND timestamp > now() - INTERVAL 60 DAY GROUP BY id, email HAVING count() >= 3 AND dateDiff('day', max(timestamp), now()) > 14 ORDER BY days_since_last_seen DESC LIMIT 20",{"type":52,"tag":141,"props":945,"children":946},{},[947,952],{"type":52,"tag":66,"props":948,"children":949},{},[950],{"type":57,"value":951},"Build a balanced sample.",{"type":57,"value":953}," Unless the user asked for one specific segment, mixing 5 heavy users + 3 drop-offs + 2 at-risk users yields the most actionable interviews: you learn what works, what blocks adoption, and what breaks retention. Adjust counts to match what the user actually wants.",{"type":52,"tag":60,"props":955,"children":956},{},[957,959,964,966,971,973,978,980,985,987,993,995,1000,1002,1007,1009,1014,1016,1021],{"type":57,"value":958},"Route each row by its ",{"type":52,"tag":74,"props":960,"children":962},{"className":961},[],[963],{"type":57,"value":860},{"type":57,"value":965}," column: rows where ",{"type":52,"tag":74,"props":967,"children":969},{"className":968},[],[970],{"type":57,"value":860},{"type":57,"value":972}," is non-null go into ",{"type":52,"tag":74,"props":974,"children":976},{"className":975},[],[977],{"type":57,"value":222},{"type":57,"value":979},", and rows where ",{"type":52,"tag":74,"props":981,"children":983},{"className":982},[],[984],{"type":57,"value":860},{"type":57,"value":986}," is null (so ",{"type":52,"tag":74,"props":988,"children":990},{"className":989},[],[991],{"type":57,"value":992},"id",{"type":57,"value":994}," holds the ",{"type":52,"tag":74,"props":996,"children":998},{"className":997},[],[999],{"type":57,"value":829},{"type":57,"value":1001},") go into ",{"type":52,"tag":74,"props":1003,"children":1005},{"className":1004},[],[1006],{"type":57,"value":233},{"type":57,"value":1008}," — both can be set on the same topic. Keep ",{"type":52,"tag":74,"props":1010,"children":1012},{"className":1011},[],[1013],{"type":57,"value":868},{"type":57,"value":1015}," and ",{"type":52,"tag":74,"props":1017,"children":1019},{"className":1018},[],[1020],{"type":57,"value":883},{"type":57,"value":1022}," per person so Step 5 can synthesise context like \"used checkout 47 times in last 60 days; last seen 2 days ago\".",{"type":52,"tag":118,"props":1024,"children":1026},{"id":1025},"step-3-capture-the-topic",[1027],{"type":57,"value":1028},"Step 3: Capture the topic",{"type":52,"tag":60,"props":1030,"children":1031},{},[1032,1037],{"type":52,"tag":74,"props":1033,"children":1035},{"className":1034},[],[1036],{"type":57,"value":251},{"type":57,"value":1038}," is one or two sentences describing what the interview is about. Infer from context where possible — don't ask the user to repeat themselves.",{"type":52,"tag":60,"props":1040,"children":1041},{},[1042,1044,1050],{"type":57,"value":1043},"Example: \"ask trial users why they didn't convert\" → ",{"type":52,"tag":74,"props":1045,"children":1047},{"className":1046},[],[1048],{"type":57,"value":1049},"topic: \"Why trial users didn't convert in the first 14 days\"",{"type":57,"value":716},{"type":52,"tag":118,"props":1052,"children":1054},{"id":1053},"step-4-prepare-the-voice-agent",[1055],{"type":57,"value":1056},"Step 4: Prepare the voice agent",{"type":52,"tag":60,"props":1058,"children":1059},{},[1060,1062],{"type":57,"value":1061},"Two fields shape what the agent actually does on the call. ",{"type":52,"tag":66,"props":1063,"children":1064},{},[1065],{"type":57,"value":1066},"Always ask about both before creating the topic.",{"type":52,"tag":516,"props":1068,"children":1070},{"id":1069},"always-ask-what-questions-do-they-want-to-ask",[1071],{"type":57,"value":1072},"Always ask: what questions do they want to ask?",{"type":52,"tag":60,"props":1074,"children":1075},{},[1076,1081],{"type":52,"tag":74,"props":1077,"children":1079},{"className":1078},[],[1080],{"type":57,"value":287},{"type":57,"value":1082}," is an ordered list the agent works through. Anchors, not a script — the agent will adapt phrasing. Keep them open-ended:",{"type":52,"tag":137,"props":1084,"children":1085},{},[1086,1091],{"type":52,"tag":141,"props":1087,"children":1088},{},[1089],{"type":57,"value":1090},"✅ \"What made you decide to try PostHog?\"",{"type":52,"tag":141,"props":1092,"children":1093},{},[1094],{"type":57,"value":1095},"❌ \"Did you like PostHog?\"",{"type":52,"tag":60,"props":1097,"children":1098},{},[1099,1101],{"type":57,"value":1100},"If the user already listed questions in their original request, use those and confirm. Otherwise, ask explicitly: ",{"type":52,"tag":88,"props":1102,"children":1103},{},[1104],{"type":57,"value":1105},"\"What questions do you want the agent to ask?\"",{"type":52,"tag":60,"props":1107,"children":1108},{},[1109,1111,1116],{"type":57,"value":1110},"If the user can't think of any, suggest 3–5 open-ended questions drawn from the ",{"type":52,"tag":74,"props":1112,"children":1114},{"className":1113},[],[1115],{"type":57,"value":251},{"type":57,"value":1117}," and offer them for review before creating.",{"type":52,"tag":60,"props":1119,"children":1120},{},[1121],{"type":57,"value":1122},"The field is technically optional in the API, but don't skip it silently — an interview with no questions is rarely useful.",{"type":52,"tag":60,"props":1124,"children":1125},{},[1126],{"type":57,"value":1127},"Question templates by research goal:",{"type":52,"tag":137,"props":1129,"children":1130},{},[1131,1182,1231],{"type":52,"tag":141,"props":1132,"children":1133},{},[1134,1139,1141],{"type":52,"tag":66,"props":1135,"children":1136},{},[1137],{"type":57,"value":1138},"Why users dropped off \u002F churned",{"type":57,"value":1140},":\n",{"type":52,"tag":137,"props":1142,"children":1143},{},[1144,1156,1161,1166,1171],{"type":52,"tag":141,"props":1145,"children":1146},{},[1147,1149,1154],{"type":57,"value":1148},"\"Tell me about the last time you tried ",{"type":52,"tag":566,"props":1150,"children":1151},{},[1152],{"type":57,"value":1153},"feature",{"type":57,"value":1155}," — what were you trying to do?\"",{"type":52,"tag":141,"props":1157,"children":1158},{},[1159],{"type":57,"value":1160},"\"Walk me through what happened step-by-step.\"",{"type":52,"tag":141,"props":1162,"children":1163},{},[1164],{"type":57,"value":1165},"\"What did you expect vs what actually happened?\"",{"type":52,"tag":141,"props":1167,"children":1168},{},[1169],{"type":57,"value":1170},"\"What made you stop or decide not to continue?\"",{"type":52,"tag":141,"props":1172,"children":1173},{},[1174,1176,1180],{"type":57,"value":1175},"\"What would need to change for you to use ",{"type":52,"tag":566,"props":1177,"children":1178},{},[1179],{"type":57,"value":1153},{"type":57,"value":1181}," regularly?\"",{"type":52,"tag":141,"props":1183,"children":1184},{},[1185,1190,1191],{"type":52,"tag":66,"props":1186,"children":1187},{},[1188],{"type":57,"value":1189},"Why heavy users love a feature",{"type":57,"value":1140},{"type":52,"tag":137,"props":1192,"children":1193},{},[1194,1205,1210,1221,1226],{"type":52,"tag":141,"props":1195,"children":1196},{},[1197,1199,1203],{"type":57,"value":1198},"\"Tell me about how you use ",{"type":52,"tag":566,"props":1200,"children":1201},{},[1202],{"type":57,"value":1153},{"type":57,"value":1204}," — what problem does it solve for you?\"",{"type":52,"tag":141,"props":1206,"children":1207},{},[1208],{"type":57,"value":1209},"\"Walk me through your typical workflow.\"",{"type":52,"tag":141,"props":1211,"children":1212},{},[1213,1215,1219],{"type":57,"value":1214},"\"What would you do if ",{"type":52,"tag":566,"props":1216,"children":1217},{},[1218],{"type":57,"value":1153},{"type":57,"value":1220}," didn't exist?\"",{"type":52,"tag":141,"props":1222,"children":1223},{},[1224],{"type":57,"value":1225},"\"What almost made you not use it when you first tried?\"",{"type":52,"tag":141,"props":1227,"children":1228},{},[1229],{"type":57,"value":1230},"\"What's one thing you wish it did differently?\"",{"type":52,"tag":141,"props":1232,"children":1233},{},[1234,1239,1240],{"type":52,"tag":66,"props":1235,"children":1236},{},[1237],{"type":57,"value":1238},"Why someone hasn't tried a feature yet",{"type":57,"value":1140},{"type":52,"tag":137,"props":1241,"children":1242},{},[1243,1254,1259],{"type":52,"tag":141,"props":1244,"children":1245},{},[1246,1248,1252],{"type":57,"value":1247},"\"Have you noticed ",{"type":52,"tag":566,"props":1249,"children":1250},{},[1251],{"type":57,"value":1153},{"type":57,"value":1253}," in the product?\"",{"type":52,"tag":141,"props":1255,"children":1256},{},[1257],{"type":57,"value":1258},"\"What's stopped you from trying it?\"",{"type":52,"tag":141,"props":1260,"children":1261},{},[1262],{"type":57,"value":1263},"\"What would have to be true for it to be worth trying?\"",{"type":52,"tag":516,"props":1265,"children":1267},{"id":1266},"always-offer-extra-context-to-guide-the-interview",[1268],{"type":57,"value":1269},"Always offer: extra context to guide the interview",{"type":52,"tag":60,"props":1271,"children":1272},{},[1273,1278],{"type":52,"tag":74,"props":1274,"children":1276},{"className":1275},[],[1277],{"type":57,"value":269},{"type":57,"value":1279}," is optional, but a few sentences here make the conversation dramatically better. Always offer the user the chance to provide it, e.g.:",{"type":52,"tag":1281,"props":1282,"children":1283},"blockquote",{},[1284],{"type":52,"tag":60,"props":1285,"children":1286},{},[1287],{"type":52,"tag":88,"props":1288,"children":1289},{},[1290],{"type":57,"value":1291},"\"Want to give the agent any extra context? Things like tone, what to avoid, or background on the interviewee help guide the conversation. It's optional.\"",{"type":52,"tag":60,"props":1293,"children":1294},{},[1295],{"type":57,"value":1296},"Useful kinds of context:",{"type":52,"tag":137,"props":1298,"children":1299},{},[1300,1310,1320,1330],{"type":52,"tag":141,"props":1301,"children":1302},{},[1303,1308],{"type":52,"tag":66,"props":1304,"children":1305},{},[1306],{"type":57,"value":1307},"Tone",{"type":57,"value":1309},": \"warm and conversational\", \"skip pleasantries — this is a 10-minute call\"",{"type":52,"tag":141,"props":1311,"children":1312},{},[1313,1318],{"type":52,"tag":66,"props":1314,"children":1315},{},[1316],{"type":57,"value":1317},"Constraints",{"type":57,"value":1319},": \"don't promise feature delivery\", \"do not discuss pricing\"",{"type":52,"tag":141,"props":1321,"children":1322},{},[1323,1328],{"type":52,"tag":66,"props":1324,"children":1325},{},[1326],{"type":57,"value":1327},"Background the agent should know",{"type":57,"value":1329},": \"the user just churned from the Scale plan; be empathetic\", \"this person tried PostHog 6 months ago and bounced\"",{"type":52,"tag":141,"props":1331,"children":1332},{},[1333,1338],{"type":52,"tag":66,"props":1334,"children":1335},{},[1336],{"type":57,"value":1337},"Persona",{"type":57,"value":1339},": \"you are Sam, a PostHog product researcher\"",{"type":52,"tag":60,"props":1341,"children":1342},{},[1343,1345,1350],{"type":57,"value":1344},"If the user declines, that's fine — leave ",{"type":52,"tag":74,"props":1346,"children":1348},{"className":1347},[],[1349],{"type":57,"value":269},{"type":57,"value":1351}," empty and continue.",{"type":52,"tag":118,"props":1353,"children":1355},{"id":1354},"calling-user-interview-topics-create",[1356],{"type":57,"value":1357},"Calling user-interview-topics-create",{"type":52,"tag":60,"props":1359,"children":1360},{},[1361],{"type":57,"value":1362},"Once you have the pieces:",{"type":52,"tag":555,"props":1364,"children":1368},{"className":1365,"code":1366,"language":1367,"meta":560,"style":560},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"topic\": \"Why trial users churned in week 2\",\n  \"interviewee_emails\": [\"paul@acme.com\", \"alex@beta.com\"],\n  \"interviewee_distinct_ids\": [\"distinct-id-with-no-email\"],\n  \"agent_context\": \"Be warm. The interviewee just churned — don't pitch.\",\n  \"questions\": [\n    \"What were you hoping PostHog would help with?\",\n    \"Where did you get stuck?\",\n    \"What would have made you stay?\"\n  ]\n}\n","json",[1369],{"type":52,"tag":74,"props":1370,"children":1371},{"__ignoreMap":560},[1372,1381,1424,1484,1524,1561,1586,1608,1629,1647,1656],{"type":52,"tag":566,"props":1373,"children":1374},{"class":568,"line":569},[1375],{"type":52,"tag":566,"props":1376,"children":1378},{"style":1377},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[1379],{"type":57,"value":1380},"{\n",{"type":52,"tag":566,"props":1382,"children":1383},{"class":568,"line":602},[1384,1389,1394,1399,1404,1409,1415,1419],{"type":52,"tag":566,"props":1385,"children":1386},{"style":1377},[1387],{"type":57,"value":1388},"  \"",{"type":52,"tag":566,"props":1390,"children":1392},{"style":1391},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[1393],{"type":57,"value":251},{"type":52,"tag":566,"props":1395,"children":1396},{"style":1377},[1397],{"type":57,"value":1398},"\"",{"type":52,"tag":566,"props":1400,"children":1401},{"style":1377},[1402],{"type":57,"value":1403},":",{"type":52,"tag":566,"props":1405,"children":1406},{"style":1377},[1407],{"type":57,"value":1408}," \"",{"type":52,"tag":566,"props":1410,"children":1412},{"style":1411},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[1413],{"type":57,"value":1414},"Why trial users churned in week 2",{"type":52,"tag":566,"props":1416,"children":1417},{"style":1377},[1418],{"type":57,"value":1398},{"type":52,"tag":566,"props":1420,"children":1421},{"style":1377},[1422],{"type":57,"value":1423},",\n",{"type":52,"tag":566,"props":1425,"children":1426},{"class":568,"line":611},[1427,1431,1435,1439,1443,1448,1452,1457,1461,1466,1470,1475,1479],{"type":52,"tag":566,"props":1428,"children":1429},{"style":1377},[1430],{"type":57,"value":1388},{"type":52,"tag":566,"props":1432,"children":1433},{"style":1391},[1434],{"type":57,"value":222},{"type":52,"tag":566,"props":1436,"children":1437},{"style":1377},[1438],{"type":57,"value":1398},{"type":52,"tag":566,"props":1440,"children":1441},{"style":1377},[1442],{"type":57,"value":1403},{"type":52,"tag":566,"props":1444,"children":1445},{"style":1377},[1446],{"type":57,"value":1447}," [",{"type":52,"tag":566,"props":1449,"children":1450},{"style":1377},[1451],{"type":57,"value":1398},{"type":52,"tag":566,"props":1453,"children":1454},{"style":1411},[1455],{"type":57,"value":1456},"paul@acme.com",{"type":52,"tag":566,"props":1458,"children":1459},{"style":1377},[1460],{"type":57,"value":1398},{"type":52,"tag":566,"props":1462,"children":1463},{"style":1377},[1464],{"type":57,"value":1465},",",{"type":52,"tag":566,"props":1467,"children":1468},{"style":1377},[1469],{"type":57,"value":1408},{"type":52,"tag":566,"props":1471,"children":1472},{"style":1411},[1473],{"type":57,"value":1474},"alex@beta.com",{"type":52,"tag":566,"props":1476,"children":1477},{"style":1377},[1478],{"type":57,"value":1398},{"type":52,"tag":566,"props":1480,"children":1481},{"style":1377},[1482],{"type":57,"value":1483},"],\n",{"type":52,"tag":566,"props":1485,"children":1486},{"class":568,"line":620},[1487,1491,1495,1499,1503,1507,1511,1516,1520],{"type":52,"tag":566,"props":1488,"children":1489},{"style":1377},[1490],{"type":57,"value":1388},{"type":52,"tag":566,"props":1492,"children":1493},{"style":1391},[1494],{"type":57,"value":233},{"type":52,"tag":566,"props":1496,"children":1497},{"style":1377},[1498],{"type":57,"value":1398},{"type":52,"tag":566,"props":1500,"children":1501},{"style":1377},[1502],{"type":57,"value":1403},{"type":52,"tag":566,"props":1504,"children":1505},{"style":1377},[1506],{"type":57,"value":1447},{"type":52,"tag":566,"props":1508,"children":1509},{"style":1377},[1510],{"type":57,"value":1398},{"type":52,"tag":566,"props":1512,"children":1513},{"style":1411},[1514],{"type":57,"value":1515},"distinct-id-with-no-email",{"type":52,"tag":566,"props":1517,"children":1518},{"style":1377},[1519],{"type":57,"value":1398},{"type":52,"tag":566,"props":1521,"children":1522},{"style":1377},[1523],{"type":57,"value":1483},{"type":52,"tag":566,"props":1525,"children":1527},{"class":568,"line":1526},5,[1528,1532,1536,1540,1544,1548,1553,1557],{"type":52,"tag":566,"props":1529,"children":1530},{"style":1377},[1531],{"type":57,"value":1388},{"type":52,"tag":566,"props":1533,"children":1534},{"style":1391},[1535],{"type":57,"value":269},{"type":52,"tag":566,"props":1537,"children":1538},{"style":1377},[1539],{"type":57,"value":1398},{"type":52,"tag":566,"props":1541,"children":1542},{"style":1377},[1543],{"type":57,"value":1403},{"type":52,"tag":566,"props":1545,"children":1546},{"style":1377},[1547],{"type":57,"value":1408},{"type":52,"tag":566,"props":1549,"children":1550},{"style":1411},[1551],{"type":57,"value":1552},"Be warm. The interviewee just churned — don't pitch.",{"type":52,"tag":566,"props":1554,"children":1555},{"style":1377},[1556],{"type":57,"value":1398},{"type":52,"tag":566,"props":1558,"children":1559},{"style":1377},[1560],{"type":57,"value":1423},{"type":52,"tag":566,"props":1562,"children":1564},{"class":568,"line":1563},6,[1565,1569,1573,1577,1581],{"type":52,"tag":566,"props":1566,"children":1567},{"style":1377},[1568],{"type":57,"value":1388},{"type":52,"tag":566,"props":1570,"children":1571},{"style":1391},[1572],{"type":57,"value":287},{"type":52,"tag":566,"props":1574,"children":1575},{"style":1377},[1576],{"type":57,"value":1398},{"type":52,"tag":566,"props":1578,"children":1579},{"style":1377},[1580],{"type":57,"value":1403},{"type":52,"tag":566,"props":1582,"children":1583},{"style":1377},[1584],{"type":57,"value":1585}," [\n",{"type":52,"tag":566,"props":1587,"children":1589},{"class":568,"line":1588},7,[1590,1595,1600,1604],{"type":52,"tag":566,"props":1591,"children":1592},{"style":1377},[1593],{"type":57,"value":1594},"    \"",{"type":52,"tag":566,"props":1596,"children":1597},{"style":1411},[1598],{"type":57,"value":1599},"What were you hoping PostHog would help with?",{"type":52,"tag":566,"props":1601,"children":1602},{"style":1377},[1603],{"type":57,"value":1398},{"type":52,"tag":566,"props":1605,"children":1606},{"style":1377},[1607],{"type":57,"value":1423},{"type":52,"tag":566,"props":1609,"children":1611},{"class":568,"line":1610},8,[1612,1616,1621,1625],{"type":52,"tag":566,"props":1613,"children":1614},{"style":1377},[1615],{"type":57,"value":1594},{"type":52,"tag":566,"props":1617,"children":1618},{"style":1411},[1619],{"type":57,"value":1620},"Where did you get stuck?",{"type":52,"tag":566,"props":1622,"children":1623},{"style":1377},[1624],{"type":57,"value":1398},{"type":52,"tag":566,"props":1626,"children":1627},{"style":1377},[1628],{"type":57,"value":1423},{"type":52,"tag":566,"props":1630,"children":1632},{"class":568,"line":1631},9,[1633,1637,1642],{"type":52,"tag":566,"props":1634,"children":1635},{"style":1377},[1636],{"type":57,"value":1594},{"type":52,"tag":566,"props":1638,"children":1639},{"style":1411},[1640],{"type":57,"value":1641},"What would have made you stay?",{"type":52,"tag":566,"props":1643,"children":1644},{"style":1377},[1645],{"type":57,"value":1646},"\"\n",{"type":52,"tag":566,"props":1648,"children":1650},{"class":568,"line":1649},10,[1651],{"type":52,"tag":566,"props":1652,"children":1653},{"style":1377},[1654],{"type":57,"value":1655},"  ]\n",{"type":52,"tag":566,"props":1657,"children":1659},{"class":568,"line":1658},11,[1660],{"type":52,"tag":566,"props":1661,"children":1662},{"style":1377},[1663],{"type":57,"value":1664},"}\n",{"type":52,"tag":60,"props":1666,"children":1667},{},[1668],{"type":57,"value":1669},"After creation, capture the returned topic ID — you'll need it for Step 5 and for handing off to the voice agent.",{"type":52,"tag":118,"props":1671,"children":1673},{"id":1672},"step-5-optionally-attach-per-interviewee-context",[1674],{"type":57,"value":1675},"Step 5: Optionally attach per-interviewee context",{"type":52,"tag":60,"props":1677,"children":1678},{},[1679,1681,1686,1688,1694],{"type":57,"value":1680},"The topic-level ",{"type":52,"tag":74,"props":1682,"children":1684},{"className":1683},[],[1685],{"type":57,"value":269},{"type":57,"value":1687}," applies to every interviewee. If the user knows something specific about individual interviewees that should shape that one conversation, attach it as a per-interviewee row via ",{"type":52,"tag":74,"props":1689,"children":1691},{"className":1690},[],[1692],{"type":57,"value":1693},"user-interview-topics-interviewees-create",{"type":57,"value":1695},". This is optional — most topics won't need it.",{"type":52,"tag":60,"props":1697,"children":1698},{},[1699,1701,1707,1709,1714],{"type":57,"value":1700},"Each row pairs an ",{"type":52,"tag":74,"props":1702,"children":1704},{"className":1703},[],[1705],{"type":57,"value":1706},"interviewee_identifier",{"type":57,"value":1708}," (must match one of the emails or distinct IDs in the parent topic's targeting) with an ",{"type":52,"tag":74,"props":1710,"children":1712},{"className":1711},[],[1713],{"type":57,"value":269},{"type":57,"value":1715}," string. At most one row per (topic, interviewee). A user can have zero rows.",{"type":52,"tag":60,"props":1717,"children":1718},{},[1719],{"type":57,"value":1720},"Good per-interviewee context looks like:",{"type":52,"tag":137,"props":1722,"children":1723},{},[1724,1729,1734],{"type":52,"tag":141,"props":1725,"children":1726},{},[1727],{"type":57,"value":1728},"\"uses the replay product but has never used summarization\"",{"type":52,"tag":141,"props":1730,"children":1731},{},[1732],{"type":57,"value":1733},"\"churned from Scale plan last month — be empathetic, don't pitch\"",{"type":52,"tag":141,"props":1735,"children":1736},{},[1737],{"type":57,"value":1738},"\"founder, very technical, skip basic product explanations\"",{"type":52,"tag":60,"props":1740,"children":1741},{},[1742,1744],{"type":57,"value":1743},"After Step 4 succeeds, ask the user: ",{"type":52,"tag":88,"props":1745,"children":1746},{},[1747],{"type":57,"value":1748},"\"Want to add per-interviewee context? Useful when individual people have very different backgrounds. You can either dictate the rows or paste a CSV.\"",{"type":52,"tag":60,"props":1750,"children":1751},{},[1752,1754,1760,1762,1768],{"type":57,"value":1753},"If you found the audience via behavioral query in Step 2, you already have per-person context (usage counts, dormancy windows). Use it: e.g. ",{"type":52,"tag":74,"props":1755,"children":1757},{"className":1756},[],[1758],{"type":57,"value":1759},"\"used checkout 47 times in last 60 days; last seen 2 days ago\"",{"type":57,"value":1761}," for heavy users, ",{"type":52,"tag":74,"props":1763,"children":1765},{"className":1764},[],[1766],{"type":57,"value":1767},"\"tried checkout once 18 days ago, never returned\"",{"type":57,"value":1769}," for drop-offs.",{"type":52,"tag":516,"props":1771,"children":1773},{"id":1772},"accepting-csv-input",[1774],{"type":57,"value":1775},"Accepting CSV input",{"type":52,"tag":60,"props":1777,"children":1778},{},[1779,1781,1787],{"type":57,"value":1780},"If the user pastes a CSV, expect two columns: ",{"type":52,"tag":74,"props":1782,"children":1784},{"className":1783},[],[1785],{"type":57,"value":1786},"identifier,context",{"type":57,"value":1788},". Either with or without a header row. Examples:",{"type":52,"tag":555,"props":1790,"children":1794},{"className":1791,"code":1792,"language":1793,"meta":560,"style":560},"language-csv shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","paul@acme.com,uses replay but never summarization\nsteve@apple.com,founder; very technical; skip product basics\n","csv",[1795],{"type":52,"tag":74,"props":1796,"children":1797},{"__ignoreMap":560},[1798,1806],{"type":52,"tag":566,"props":1799,"children":1800},{"class":568,"line":569},[1801],{"type":52,"tag":566,"props":1802,"children":1803},{},[1804],{"type":57,"value":1805},"paul@acme.com,uses replay but never summarization\n",{"type":52,"tag":566,"props":1807,"children":1808},{"class":568,"line":602},[1809],{"type":52,"tag":566,"props":1810,"children":1811},{},[1812],{"type":57,"value":1813},"steve@apple.com,founder; very technical; skip product basics\n",{"type":52,"tag":60,"props":1815,"children":1816},{},[1817],{"type":57,"value":1818},"Or with a header:",{"type":52,"tag":555,"props":1820,"children":1822},{"className":1791,"code":1821,"language":1793,"meta":560,"style":560},"identifier,context\nabc-distinct-id-1,churned from Scale last month — be empathetic\n",[1823],{"type":52,"tag":74,"props":1824,"children":1825},{"__ignoreMap":560},[1826,1834],{"type":52,"tag":566,"props":1827,"children":1828},{"class":568,"line":569},[1829],{"type":52,"tag":566,"props":1830,"children":1831},{},[1832],{"type":57,"value":1833},"identifier,context\n",{"type":52,"tag":566,"props":1835,"children":1836},{"class":568,"line":602},[1837],{"type":52,"tag":566,"props":1838,"children":1839},{},[1840],{"type":57,"value":1841},"abc-distinct-id-1,churned from Scale last month — be empathetic\n",{"type":52,"tag":60,"props":1843,"children":1844},{},[1845,1847,1853,1855,1861,1863,1869,1871,1877,1879,1885,1886,1892,1894,1900,1902,1907],{"type":57,"value":1846},"Parse the CSV, then create the rows with the captured ",{"type":52,"tag":74,"props":1848,"children":1850},{"className":1849},[],[1851],{"type":57,"value":1852},"topic_id",{"type":57,"value":1854},". For more than a couple of rows, prefer ",{"type":52,"tag":74,"props":1856,"children":1858},{"className":1857},[],[1859],{"type":57,"value":1860},"user-interview-topics-interviewees-bulk-create",{"type":57,"value":1862}," — it takes an ",{"type":52,"tag":74,"props":1864,"children":1866},{"className":1865},[],[1867],{"type":57,"value":1868},"items",{"type":57,"value":1870}," array of ",{"type":52,"tag":74,"props":1872,"children":1874},{"className":1873},[],[1875],{"type":57,"value":1876},"(interviewee_identifier, agent_context)",{"type":57,"value":1878}," and creates up to 500 rows in one request, reporting ",{"type":52,"tag":74,"props":1880,"children":1882},{"className":1881},[],[1883],{"type":57,"value":1884},"inserted_count",{"type":57,"value":870},{"type":52,"tag":74,"props":1887,"children":1889},{"className":1888},[],[1890],{"type":57,"value":1891},"skipped_count",{"type":57,"value":1893},", and ",{"type":52,"tag":74,"props":1895,"children":1897},{"className":1896},[],[1898],{"type":57,"value":1899},"skipped_identifiers",{"type":57,"value":1901}," for pairs that already exist. Fall back to ",{"type":52,"tag":74,"props":1903,"children":1905},{"className":1904},[],[1906],{"type":57,"value":1693},{"type":57,"value":1908}," for a single row. Skip blank lines. Quote-escape commas inside the context cell — standard CSV rules.",{"type":52,"tag":60,"props":1910,"children":1911},{},[1912,1914,1919,1920,1925],{"type":57,"value":1913},"If a row's identifier isn't present in the parent topic's ",{"type":52,"tag":74,"props":1915,"children":1917},{"className":1916},[],[1918],{"type":57,"value":222},{"type":57,"value":94},{"type":52,"tag":74,"props":1921,"children":1923},{"className":1922},[],[1924],{"type":57,"value":233},{"type":57,"value":1926},", warn the user before creating — the voice agent looks up context by exact string match, so a mismatched identifier just gets ignored at runtime.",{"type":52,"tag":118,"props":1928,"children":1930},{"id":1929},"edge-cases",[1931],{"type":57,"value":1932},"Edge cases",{"type":52,"tag":137,"props":1934,"children":1935},{},[1936,1946,1963,2002],{"type":52,"tag":141,"props":1937,"children":1938},{},[1939,1944],{"type":52,"tag":66,"props":1940,"children":1941},{},[1942],{"type":57,"value":1943},"No users match the behavioral query.",{"type":57,"value":1945}," Possible reasons: the event isn't firing, the date range is too narrow, or no users have email addresses captured as person properties. Offer to widen the date range, try a different event, or fall back to cohorts \u002F explicit emails.",{"type":52,"tag":141,"props":1947,"children":1948},{},[1949,1954,1956,1961],{"type":52,"tag":66,"props":1950,"children":1951},{},[1952],{"type":57,"value":1953},"Users matched but few have emails.",{"type":57,"value":1955}," PostHog stores whatever the SDK captures. If only a handful of matching users have email addresses on their person profile, surface the count and ask: take the smaller sample, fall back to ",{"type":52,"tag":74,"props":1957,"children":1959},{"className":1958},[],[1960],{"type":57,"value":233},{"type":57,"value":1962}," (the agent can still reach them via in-app delivery), or skip the behavioral query and let the user paste emails directly.",{"type":52,"tag":141,"props":1964,"children":1965},{},[1966,1971,1973,1978,1980,1986,1987,1993,1994,2000],{"type":52,"tag":66,"props":1967,"children":1968},{},[1969],{"type":57,"value":1970},"Ambiguous event name.",{"type":57,"value":1972}," If ",{"type":52,"tag":74,"props":1974,"children":1976},{"className":1975},[],[1977],{"type":57,"value":789},{"type":57,"value":1979}," returns multiple candidates (e.g. ",{"type":52,"tag":74,"props":1981,"children":1983},{"className":1982},[],[1984],{"type":57,"value":1985},"checkout_started",{"type":57,"value":870},{"type":52,"tag":74,"props":1988,"children":1990},{"className":1989},[],[1991],{"type":57,"value":1992},"checkout_completed",{"type":57,"value":870},{"type":52,"tag":74,"props":1995,"children":1997},{"className":1996},[],[1998],{"type":57,"value":1999},"checkout_abandoned",{"type":57,"value":2001},"), list them with counts and let the user pick the behavior they want to understand. Don't pick silently.",{"type":52,"tag":141,"props":2003,"children":2004},{},[2005,2010],{"type":52,"tag":66,"props":2006,"children":2007},{},[2008],{"type":57,"value":2009},"User asks to interview only drop-offs (or only one segment).",{"type":57,"value":2011}," That works, but flag the tradeoff: interviewing only drop-offs tells you what's broken without telling you what works. Recommend including 2–3 successful users for contrast unless the user has a reason for the narrower sample.",{"type":52,"tag":118,"props":2013,"children":2015},{"id":2014},"what-this-skill-is-not-for",[2016],{"type":57,"value":2017},"What this skill is not for",{"type":52,"tag":137,"props":2019,"children":2020},{},[2021,2047,2086],{"type":52,"tag":141,"props":2022,"children":2023},{},[2024,2029,2031,2037,2039,2045],{"type":52,"tag":66,"props":2025,"children":2026},{},[2027],{"type":57,"value":2028},"Uploading a recorded interview",{"type":57,"value":2030}," — that's the separate ",{"type":52,"tag":74,"props":2032,"children":2034},{"className":2033},[],[2035],{"type":57,"value":2036},"UserInterview",{"type":57,"value":2038}," model (",{"type":52,"tag":74,"props":2040,"children":2042},{"className":2041},[],[2043],{"type":57,"value":2044},"user_interviews_create",{"type":57,"value":2046}," with an audio file). Different flow, different model.",{"type":52,"tag":141,"props":2048,"children":2049},{},[2050,2055,2056,2062,2064,2070,2071,2077,2078,2084],{"type":52,"tag":66,"props":2051,"children":2052},{},[2053],{"type":57,"value":2054},"Listing existing topics",{"type":57,"value":245},{"type":52,"tag":74,"props":2057,"children":2059},{"className":2058},[],[2060],{"type":57,"value":2061},"user-interview-topics-list",{"type":57,"value":2063}," handles that directly with ",{"type":52,"tag":74,"props":2065,"children":2067},{"className":2066},[],[2068],{"type":57,"value":2069},"search",{"type":57,"value":870},{"type":52,"tag":74,"props":2072,"children":2074},{"className":2073},[],[2075],{"type":57,"value":2076},"limit",{"type":57,"value":1893},{"type":52,"tag":74,"props":2079,"children":2081},{"className":2080},[],[2082],{"type":57,"value":2083},"offset",{"type":57,"value":2085},". No skill needed.",{"type":52,"tag":141,"props":2087,"children":2088},{},[2089,2094,2096,2101],{"type":52,"tag":66,"props":2090,"children":2091},{},[2092],{"type":57,"value":2093},"Analyzing transcripts after the interview",{"type":57,"value":2095}," — out of scope here; that lives with the recorded ",{"type":52,"tag":74,"props":2097,"children":2099},{"className":2098},[],[2100],{"type":57,"value":2036},{"type":57,"value":2102}," flow.",{"type":52,"tag":2104,"props":2105,"children":2106},"style",{},[2107],{"type":57,"value":2108},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2110,"total":2278},[2111,2127,2139,2151,2164,2179,2195,2212,2226,2241,2251,2268],{"slug":2112,"name":2112,"fn":2113,"description":2114,"org":2115,"tags":2116,"stars":20,"repoUrl":21,"updatedAt":2126},"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},[2117,2119,2122,2125],{"name":2118,"slug":28,"type":13},"Analytics",{"name":2120,"slug":2121,"type":13},"Cost Optimization","cost-optimization",{"name":2123,"slug":2124,"type":13},"Observability","observability",{"name":9,"slug":8,"type":13},"2026-07-28T05:34:11.117757",{"slug":2128,"name":2128,"fn":2129,"description":2130,"org":2131,"tags":2132,"stars":20,"repoUrl":21,"updatedAt":2138},"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},[2133,2134,2137],{"name":2118,"slug":28,"type":13},{"name":2135,"slug":2136,"type":13},"Audit","audit",{"name":9,"slug":8,"type":13},"2026-06-08T08:08:33.693989",{"slug":2140,"name":2140,"fn":2141,"description":2142,"org":2143,"tags":2144,"stars":20,"repoUrl":21,"updatedAt":2150},"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},[2145,2146,2148,2149],{"name":2135,"slug":2136,"type":13},{"name":2147,"slug":30,"type":13},"Data Warehouse",{"name":2123,"slug":2124,"type":13},{"name":9,"slug":8,"type":13},"2026-06-18T08:22:57.67984",{"slug":2152,"name":2152,"fn":2153,"description":2154,"org":2155,"tags":2156,"stars":20,"repoUrl":21,"updatedAt":2163},"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},[2157,2158,2159,2162],{"name":2135,"slug":2136,"type":13},{"name":2147,"slug":30,"type":13},{"name":2160,"slug":2161,"type":13},"Performance","performance",{"name":9,"slug":8,"type":13},"2026-06-18T08:25:10.936787",{"slug":2165,"name":2165,"fn":2166,"description":2167,"org":2168,"tags":2169,"stars":20,"repoUrl":21,"updatedAt":2178},"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},[2170,2173,2176,2177],{"name":2171,"slug":2172,"type":13},"Alerting","alerting",{"name":2174,"slug":2175,"type":13},"Debugging","debugging",{"name":2123,"slug":2124,"type":13},{"name":9,"slug":8,"type":13},"2026-06-18T08:24:40.318583",{"slug":2180,"name":2180,"fn":2181,"description":2182,"org":2183,"tags":2184,"stars":20,"repoUrl":21,"updatedAt":2194},"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},[2185,2186,2189,2190,2193],{"name":2118,"slug":28,"type":13},{"name":2187,"slug":2188,"type":13},"Monitoring","monitoring",{"name":2123,"slug":2124,"type":13},{"name":2191,"slug":2192,"type":13},"Operations","operations",{"name":9,"slug":8,"type":13},"2026-07-18T05:10:54.430898",{"slug":2196,"name":2196,"fn":2197,"description":2198,"org":2199,"tags":2200,"stars":20,"repoUrl":21,"updatedAt":2211},"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},[2201,2204,2207,2208],{"name":2202,"slug":2203,"type":13},"Automation","automation",{"name":2205,"slug":2206,"type":13},"MCP","mcp",{"name":9,"slug":8,"type":13},{"name":2209,"slug":2210,"type":13},"Workflow Automation","workflow-automation","2026-07-28T05:34:12.167015",{"slug":2213,"name":2213,"fn":2214,"description":2215,"org":2216,"tags":2217,"stars":20,"repoUrl":21,"updatedAt":2225},"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},[2218,2219,2220,2223,2224],{"name":2118,"slug":28,"type":13},{"name":2174,"slug":2175,"type":13},{"name":2221,"slug":2222,"type":13},"Frontend","frontend",{"name":2123,"slug":2124,"type":13},{"name":9,"slug":8,"type":13},"2026-05-07T05:56:19.828048",{"slug":2227,"name":2227,"fn":2228,"description":2229,"org":2230,"tags":2231,"stars":20,"repoUrl":21,"updatedAt":2240},"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},[2232,2235,2236,2237],{"name":2233,"slug":2234,"type":13},"API Development","api-development",{"name":2221,"slug":2222,"type":13},{"name":9,"slug":8,"type":13},{"name":2238,"slug":2239,"type":13},"SDK","sdk","2026-06-08T08:08:34.929454",{"slug":2242,"name":2242,"fn":2243,"description":2244,"org":2245,"tags":2246,"stars":20,"repoUrl":21,"updatedAt":2250},"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},[2247,2248,2249],{"name":2233,"slug":2234,"type":13},{"name":2191,"slug":2192,"type":13},{"name":9,"slug":8,"type":13},"2026-07-15T05:29:58.442727",{"slug":2252,"name":2252,"fn":2253,"description":2254,"org":2255,"tags":2256,"stars":20,"repoUrl":21,"updatedAt":2267},"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},[2257,2258,2260,2261,2264],{"name":2202,"slug":2203,"type":13},{"name":2259,"slug":860,"type":13},"Email",{"name":9,"slug":8,"type":13},{"name":2262,"slug":2263,"type":13},"Reporting","reporting",{"name":2265,"slug":2266,"type":13},"Slack","slack","2026-06-09T07:32:27.935712",{"slug":2269,"name":2269,"fn":2270,"description":2271,"org":2272,"tags":2273,"stars":20,"repoUrl":21,"updatedAt":2277},"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},[2274,2275,2276],{"name":2118,"slug":28,"type":13},{"name":2233,"slug":2234,"type":13},{"name":9,"slug":8,"type":13},"2026-06-08T08:08:29.624498",231,{"items":2280,"total":2330},[2281,2288,2294,2301,2308,2315,2323],{"slug":2112,"name":2112,"fn":2113,"description":2114,"org":2282,"tags":2283,"stars":20,"repoUrl":21,"updatedAt":2126},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2284,2285,2286,2287],{"name":2118,"slug":28,"type":13},{"name":2120,"slug":2121,"type":13},{"name":2123,"slug":2124,"type":13},{"name":9,"slug":8,"type":13},{"slug":2128,"name":2128,"fn":2129,"description":2130,"org":2289,"tags":2290,"stars":20,"repoUrl":21,"updatedAt":2138},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2291,2292,2293],{"name":2118,"slug":28,"type":13},{"name":2135,"slug":2136,"type":13},{"name":9,"slug":8,"type":13},{"slug":2140,"name":2140,"fn":2141,"description":2142,"org":2295,"tags":2296,"stars":20,"repoUrl":21,"updatedAt":2150},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2297,2298,2299,2300],{"name":2135,"slug":2136,"type":13},{"name":2147,"slug":30,"type":13},{"name":2123,"slug":2124,"type":13},{"name":9,"slug":8,"type":13},{"slug":2152,"name":2152,"fn":2153,"description":2154,"org":2302,"tags":2303,"stars":20,"repoUrl":21,"updatedAt":2163},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2304,2305,2306,2307],{"name":2135,"slug":2136,"type":13},{"name":2147,"slug":30,"type":13},{"name":2160,"slug":2161,"type":13},{"name":9,"slug":8,"type":13},{"slug":2165,"name":2165,"fn":2166,"description":2167,"org":2309,"tags":2310,"stars":20,"repoUrl":21,"updatedAt":2178},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2311,2312,2313,2314],{"name":2171,"slug":2172,"type":13},{"name":2174,"slug":2175,"type":13},{"name":2123,"slug":2124,"type":13},{"name":9,"slug":8,"type":13},{"slug":2180,"name":2180,"fn":2181,"description":2182,"org":2316,"tags":2317,"stars":20,"repoUrl":21,"updatedAt":2194},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2318,2319,2320,2321,2322],{"name":2118,"slug":28,"type":13},{"name":2187,"slug":2188,"type":13},{"name":2123,"slug":2124,"type":13},{"name":2191,"slug":2192,"type":13},{"name":9,"slug":8,"type":13},{"slug":2196,"name":2196,"fn":2197,"description":2198,"org":2324,"tags":2325,"stars":20,"repoUrl":21,"updatedAt":2211},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2326,2327,2328,2329],{"name":2202,"slug":2203,"type":13},{"name":2205,"slug":2206,"type":13},{"name":9,"slug":8,"type":13},{"name":2209,"slug":2210,"type":13},61]