> ## Documentation Index
> Fetch the complete documentation index at: https://subtext.fullstory.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# PostHog and Subtext: From a Signal to the Session

> Attach the Subtext session URL to PostHog, then jump from any PostHog signal — a funnel drop, an exception, an alert — straight into the user's real session.

PostHog tells you *that* a number moved: a conversion dropped, an event spiked, an exception appeared. It can't tell you *why* one specific person got stuck. Subtext can. Attach the Subtext session URL to your PostHog data and every PostHog signal becomes a jumping-off point — your agent reads the `subtext_url`, opens that person's real session, and walks the decisive moments with screenshots, the component tree, network, and console. PostHog detects; Subtext diagnoses.

<Note>
  This guide assumes the [Subtext capture snippet](/docs/install/overview) is installed and the PostHog JS SDK (`posthog-js`) is initialized.
</Note>

## Attach the session URL

Store the current Subtext session URL on your PostHog data so it travels with the signal. There are three places to attach it, depending on how precise the link needs to be.

### On the user profile

Attach the URL to the PostHog person with `posthog.identify`. Call it wherever your app already identifies the user — on auth resolution, not at module top level.

<CodeGroup>
  ```js analytics.js theme={null}
  const subtextUrl = FS('getSession', { format: 'url.now' })
  if (subtextUrl) {
    posthog.identify(user.id, { subtext_url: subtextUrl })
  }
  ```

  ```tsx React theme={null}
  useEffect(() => {
    if (!user) return
    FS('setIdentity', { uid: user.id, properties: { email: user.email } })
    const subtextUrl = FS('getSession', { format: 'url.now' })
    if (subtextUrl) {
      posthog.identify(user.id, { subtext_url: subtextUrl })
    }
  }, [user])
  ```
</CodeGroup>

### On every event

Register the URL as a super property and it rides along on every subsequent event automatically. The value is frozen at registration time; re-register it when the session or identity changes.

```js theme={null}
const subtextUrl = FS('getSession', { format: 'url.now' })
if (subtextUrl) {
  posthog.register({ subtext_url: subtextUrl })
}
```

### On a specific event

When the exact moment matters, pass the URL on a single `capture` call. `url.now` carries a timestamp, so the link opens the replay at that instant — ideal for high-signal events like errors, rage clicks, and conversion failures.

```js theme={null}
posthog.capture('checkout_failed', {
  reason: error.message,
  subtext_url: FS('getSession', { format: 'url.now' }),
})
```

<Warning>
  Don't read `FS('getSession')` at module top level — it returns `null` until the session has started. The session URL also changes on a new session and when `FS('setIdentity')` is called with a different `uid`, so re-attach on re-identify. Super properties are frozen at set-time; re-register them.
</Warning>

## From signal to session

However you first hear about a problem in PostHog, the path is the same: get to the `subtext_url`, then hand it to your agent for a Detect-vs-Diagnose read.

<AccordionGroup>
  <Accordion title="A weekly insight or subscription email">
    Your subscribed PostHog insight emails you: "Signup → Activation funnel: conversion down 8% week-over-week."

    Open the funnel and click the drop-off step's bar (or the linked "dropped" count below the chart) to view the people who dropped at that step. Open a dropped person, go to the **Properties** tab, and copy `subtext_url`. Grab a few, then hand them to your agent:

    ```text theme={null}
    PostHog says our Signup → Activation funnel dropped 8% this week. Here are sessions from
    users who fell out at the activation step:
    <paste 3–4 subtext_urls>

    Review them and find the common reason they didn't convert. Give me a Detect-vs-Diagnose
    read per session, then one synthesized hypothesis for the drop. Point to the exact moments.
    ```
  </Accordion>

  <Accordion title="An error-tracking issue">
    PostHog Error Tracking surfaces a new exception group affecting real users.

    Open the issue, open a sample event, go to **Properties**, and copy `subtext_url` — it is deep-linked to the moment the event fired.

    ```text theme={null}
    PostHog Error Tracking flagged this exception on user <USER_ID>. Session, deep-linked to the
    moment it fired: <paste subtext_url>

    Open it at that timestamp. What was the user doing in the 30s before? Did the error block
    them, or did the app recover? Is it reproducible from that flow?
    ```
  </Accordion>

  <Accordion title="An anomaly alert on a key event">
    A PostHog alert fires: "`checkout_completed` is 30% below expected."

    From the alert, pivot to users who started but did not complete checkout in the window, open their profiles, and copy `subtext_url`.

    ```text theme={null}
    A PostHog alert says checkout_completed is 30% below normal. Here are sessions from users who
    started checkout but didn't finish in that window:
    <paste subtext_urls>

    Walk each session through the checkout flow. Where exactly do they stall, and is it the same
    place every time? Tell me whether it's a bug or a UX problem.
    ```
  </Accordion>
</AccordionGroup>

<Tip>
  For a drop-off across several users, ask your agent to roll the findings into one proof document (`doc-create`) with a screenshot per session and a single shared link — then paste that back into the PostHog insight so the number carries its explanation.
</Tip>

## For agents

An autonomous agent can run the whole loop against PostHog directly: discover the friction, extract the `subtext_url`, and hand each session to the Subtext MCP. PostHog ships an official MCP server at `https://mcp.posthog.com/mcp`, authenticated with OAuth or a personal API key; it routes automatically to your US or EU data region based on the account you authenticate with.

<Steps>
  <Step title="Discover who is affected">
    Use `query-error-tracking-issues-list` for exception hotspots or `query-funnel-actors` for the users who dropped at a step. For anything else, `execute-sql` runs HogQL directly.
  </Step>

  <Step title="Extract the session URL">
    Read `subtext_url` off the affected persons or events with `persons-retrieve`, or an `execute-sql` query over `persons` / `events`:

    ```sql theme={null}
    SELECT distinct_id, properties.subtext_url, timestamp
    FROM events
    WHERE properties.subtext_url IS NOT NULL
    ORDER BY timestamp DESC
    ```
  </Step>

  <Step title="Hand off to Subtext">
    For each URL, call `review-open(session_url=<subtext_url>)` on the Subtext MCP. Event-level URLs are moment-precise, so the review opens exactly when the signal fired.
  </Step>
</Steps>

<Note>
  `subtext_url` only round-trips if it was attached at capture time (see [Attach the session URL](#attach-the-session-url)). Event-level links are moment-precise; person-level links open at session start. Match your region (US vs EU) for the API host; the MCP endpoint is the same for both regions and routes to your data region automatically based on the account you sign in with.
</Note>

## Related

* [Session Review overview](/docs/session-review/overview) — what your agent does once a session is open.
* [Install the capture snippet](/docs/install/overview) — required before any session URL exists to attach.
