> ## 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.

# Mixpanel and Subtext: From a Signal to the Session

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

Mixpanel tells you *that* a metric moved: a purchase rate dropped, an event spiked, a funnel leaked at one step. It lets you cohort the people behind the number, but it can't replay what any one of them actually experienced. Subtext can. Attach the Subtext session URL to your Mixpanel profiles and events and every Mixpanel 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. Mixpanel detects; Subtext diagnoses.

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

## Attach the session URL

Store the current Subtext session URL on your Mixpanel 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 Mixpanel person profile with `mixpanel.people.set`. Call it wherever your app already identifies the user — after identity resolves, not at module top level, and never for an anonymous user.

<CodeGroup>
  ```js analytics.js theme={null}
  const subtextUrl = FS('getSession', { format: 'url.now' })
  if (subtextUrl) {
    mixpanel.people.set({ 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) {
      mixpanel.people.set({ 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.

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

To clear it, call `mixpanel.unregister('subtext_url')`.

### On a specific event

When the exact moment matters, pass the URL directly on a single `track` 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}
mixpanel.track('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. Don't call `mixpanel.people.set` for anonymous users; attach the profile property only after the user is identified. 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 Mixpanel, the path is the same: get to the `subtext_url`, then hand it to your agent for a Detect-vs-Diagnose read. Mixpanel flagged the drop or spike; the session shows the actual cause.

<AccordionGroup>
  <Accordion title="A Mixpanel Insights alert">
    A Mixpanel alert fires by email or Slack: "`Purchase` is down 20% vs. the 7-day average."

    From the alert, open the report and build or filter to users who started but didn't purchase. Open a user profile, find `subtext_url` in the properties list, and copy it. Grab several, then hand them to your agent:

    ```text theme={null}
    Mixpanel alerted me that Purchase is down 20%. Here are sessions from users who started but
    didn't complete a purchase in that window:
    <PASTE 3-4 subtext_urls>

    Review them and find the shared failure point. Detect-vs-Diagnose: Mixpanel flagged the drop —
    what does each session actually show at the purchase step? One root-cause hypothesis at the end,
    with the moments that back it.
    ```
  </Accordion>

  <Accordion title="A scheduled board digest">
    The team's Mixpanel board arrives on a schedule and the digest shows a key conversion sliding over the week.

    Click into the report, segment to the affected users, open their profiles, and copy `subtext_url`.

    ```text theme={null}
    Our Mixpanel board shows <metric> sliding this week. Here are sessions from users in the affected
    segment:
    <PASTE subtext_urls>

    Walk each one through the relevant flow. Where do they drop, and is it consistent? Tell me if this
    is a real regression or noise, and show me the evidence.
    ```
  </Accordion>

  <Accordion title="A funnel drop-off drill-down">
    You build a Mixpanel funnel, spot a steep drop at one step, and click into the non-converters.

    Open a non-converting user's profile and copy `subtext_url`.

    ```text theme={null}
    I'm looking at a Mixpanel funnel with a steep drop at <step>. Here's a user who didn't convert
    there: <PASTE subtext_url>

    Open the session and tell me exactly what happened at that step — what they saw, what they tried,
    and why they stopped. Bug, friction, or intent? Point me to the timestamp.
    ```
  </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 Mixpanel report description so the metric carries its explanation. If the review shows the drop is user intent rather than a defect, say so plainly; it keeps engineering from chasing a step that works.
</Tip>

## For agents

An autonomous agent can run the whole loop against Mixpanel directly: discover the friction, extract the `subtext_url`, and hand each session to the Subtext MCP.

Mixpanel ships an official MCP server (hosted) documented at `docs.mixpanel.com/docs/mcp`, authenticated with OAuth or a Service Account (Service Account authentication for the MCP server is still in beta). It's good for natural-language *discovery* over funnels, flows, and session replays, and its tool registry is documented on the same page — `Run-Query` executes insights, funnels, flows, and retention queries, `Get-Report` retrieves saved reports, and `Get-User-Replays-Data` analyzes a user's session replays. None of the published MCP tools read individual profile properties, though. For deterministic `subtext_url` extraction, drive the REST Query and Engage APIs below, and use a Service Account for a headless agent.

<Steps>
  <Step title="Discover who is affected">
    Hit the Query API to locate friction. Query a saved report via the Insights Query API — `GET /api/query/insights` with `project_id` and `bookmark_id` — to read per-step conversion from a saved Funnels report, and use `/api/query/retention` for retention. The base host is `https://mixpanel.com/api/query/...` (US) or `https://eu.mixpanel.com/api/query/...` (EU), authenticated with Service Account Basic auth plus `project_id`. Avoid the Funnels (`/api/query/funnels`), Segmentation (`/api/query/segmentation`), and JQL (`/api/query/jql`) endpoints for new work — they're all in maintenance mode.
  </Step>

  <Step title="Extract the session URL">
    Read `subtext_url` per user from the Engage (Query Profiles) API:

    ```text theme={null}
    POST https://mixpanel.com/api/query/engage        # EU: https://eu.mixpanel.com/api/query/engage
    # Auth: Service Account Basic auth, project_id param
    # Params: distinct_id=<user>, output_properties=["subtext_url"]
    # → results[N].$properties.subtext_url
    ```

    `output_properties` returns just that property for a smaller, faster payload. To find every user with a session attached, pass a `where` filter like `defined(properties["subtext_url"])`.
  </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)). Match your region host (`mixpanel.com` vs `eu.mixpanel.com` / `in.mixpanel.com`) for both the Query and Engage APIs, and prefer a Service Account over OAuth for headless agents.
</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.
