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

# Segment and Subtext: From a Signal to the Session

> Attach the Subtext session URL to Segment traits and events, then jump from any Segment signal — a warehouse funnel drop, a computed audience, a Protocols alert — straight into the user's real session.

Segment is plumbing: it routes traits and events to your warehouse, BI tool, and engagement platform. It doesn't tell you *why* one person dropped or errored — the number lives downstream, in Looker or Engage, and it can't replay what the user actually saw. Subtext can. Attach the Subtext session URL to your Segment traits and events and it travels with every user and event into those downstream tools — 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. Your warehouse and audiences detect; Subtext diagnoses.

<Note>
  This guide assumes the [Subtext capture snippet](/docs/install/overview) is installed and capturing sessions, and Segment Analytics.js (`analytics.js` or `@segment/analytics-next`) is initialized.
</Note>

## Attach the session URL

Store the current Subtext session URL on your Segment data so it travels with every trait and event into your warehouse and downstream tools. There are two places to attach it, depending on how precise the link needs to be.

### On the user profile

Attach the URL to the Segment user profile via `analytics.identify`. Call it wherever your app already identifies the user — on auth resolution, not at module top level. Segment accepts any trait key, so use `subtext_url`; the warehouse connector snake\_cases column names anyway, so it lands as `subtext_url` there.

<CodeGroup>
  ```js analytics.js theme={null}
  const subtextUrl = FS('getSession', { format: 'url.now' })
  if (subtextUrl) {
    analytics.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) {
      analytics.identify(user.id, { subtext_url: subtextUrl })
    }
  }, [user])
  ```
</CodeGroup>

### On every event

Segment source middleware runs on every call, letting you attach the URL fresh per event. Because `url.now` carries a timestamp, the link deep-links to the moment each event fired. Register the middleware once at app initialization, before any `track` calls.

```js theme={null}
analytics.addSourceMiddleware(({ payload, next }) => {
  if (payload.obj.type === 'track') {
    const url = FS('getSession', { format: 'url.now' })
    if (url) payload.obj.properties = { ...payload.obj.properties, subtext_url: url }
  }
  next(payload)
})
```

<Warning>
  Don't read `FS('getSession')` at module top level — it returns `null` until the session has started. Don't switch the key mid-stream: use `subtext_url` across both `identify` and `track`, since lookups and filters are case-sensitive and a mismatched key won't be found. And don't register the middleware inside a component render — register it once at the analytics initialization point. The session URL changes on a new session and when `FS('setIdentity')` is called with a different `uid`, so if your app calls `analytics.identify` on every auth state change, the user-level linkage stays current automatically; the source middleware reads the URL fresh on every event, so it never needs re-attaching.
</Warning>

## From signal to session

Segment is plumbing, so you never hear about a problem *in* Segment — you hear about it in a downstream surface: a warehouse dashboard, a computed audience, a data-quality alert. However it reaches you, the path is the same: get to the `subtext_url`, then hand it to your agent for a Detect-vs-Diagnose read. The data says this user dropped or errored; the session shows what they actually experienced.

<AccordionGroup>
  <Accordion title="A BI dashboard flags a funnel drop">
    The analyst or growth PM reviewing the weekly funnel in Looker, Mode, or Hex sees the `checkout` funnel — built on Segment events in the warehouse — converting below trend.

    Query the event table for the affected users' sessions:

    ```sql theme={null}
    SELECT user_id, subtext_url, event, timestamp
    FROM segment.<source>.checkout_started
    WHERE subtext_url IS NOT NULL
      AND user_id IN (<dropped users>)
    ORDER BY timestamp DESC
    LIMIT 20;
    ```

    Grab a few, then hand them to your agent:

    ```text theme={null}
    Our warehouse funnel (built on Segment events) shows checkout conversion below trend. Here are
    session URLs for users who started but didn't complete:
    <PASTE 3-4 subtext_urls>

    Review them and find the common failure point. Detect-vs-Diagnose: the data says they dropped —
    what does each session actually show at the drop step? One synthesized hypothesis at the end.
    ```
  </Accordion>

  <Accordion title="A user enters a 'failed' audience in Engage">
    The lifecycle or CRM owner sees a user land in a computed audience like *Started checkout, no purchase in 24h* in Twilio Engage (or a connected tool), and wants to know *why* before triggering a win-back.

    Open the user's profile in Segment, go to **Traits**, and copy `subtext_url`.

    ```text theme={null}
    This user entered our "abandoned checkout" audience. Before I send a win-back, I want to know what
    actually happened. Session: <PASTE subtext_url>

    Open it and tell me: did they abandon by choice, or did something break/confuse them at checkout?
    If it's a bug, that changes the message I send. Walk me through the relevant moments.
    ```
  </Accordion>

  <Accordion title="A Protocols data-quality alert">
    The data engineer who owns tracking quality gets a Segment Protocols violation — a key event firing with malformed or missing properties, suggesting a broken client.

    Find a recent offending event in the source debugger or warehouse; if it carries `subtext_url`, copy it.

    ```text theme={null}
    Segment Protocols flagged malformed `Checkout Started` events from the web client. Here's a
    session where one fired: <PASTE subtext_url>

    Open it around that event. Is the client genuinely broken (bad state, JS error) or is the tracking
    just mis-wired? Show me the UI and console at that moment so I can tell a real bug from a schema
    problem.
    ```
  </Accordion>
</AccordionGroup>

<Tip>
  For a funnel investigation across several users, ask your agent to consolidate the findings into one proof document (`doc-create`) — a screenshot per dropped user and a single shareable link — then drop it into the dashboard annotation, Engage profile note, or data-quality ticket so the next person sees the human story behind the number. And if the review shows the drop was a deliberate user choice rather than a bug, say so: that redirects effort toward messaging or pricing instead of engineering.
</Tip>

## For agents

An autonomous agent can run the whole loop against Segment directly: discover the friction, extract the `subtext_url`, and hand each session to the Subtext MCP. There is **no official Segment CDP data MCP** — don't assume one exists. The only Segment-related MCP is the **Twilio MCP** (`mcp.twilio.com/docs`), a read-only documentation index whose `twilio__search` and `twilio__retrieve` tools return API specs and schemas; it helps you *find* the right Segment endpoint but can't read a trait or query an audience. The practical stance is to call Segment's REST and warehouse directly, optionally using the Twilio docs MCP to look up exact request shapes.

<Steps>
  <Step title="Discover the friction">
    Segment has no native funnel API — it's plumbing, not an analytics product. Compute drop-offs with warehouse SQL over the warehouse `tracks` tables:

    ```sql theme={null}
    -- step-to-step conversion to find the worst drop-off
    SELECT event, count(distinct user_id) AS users
    FROM segment.<source>.tracks
    WHERE timestamp > current_date - 7
    GROUP BY event ORDER BY users DESC;
    ```

    Or surface at-risk segments with computed and predictive traits via the Profile API (`?class=computed_trait`, `?class=audience`).
  </Step>

  <Step title="Extract the session URL">
    Read `subtext_url` per user from the Profile API:

    ```
    GET https://profiles.segment.com/v1/spaces/<space_id>/collections/users/profiles/<external_id>/traits?include=subtext_url
    # EU residency: https://profiles.euw1.segment.com/...
    # external_id = "user_id:abc123" or "anonymous_id:..."
    # Auth: HTTP Basic, Access Token as username, empty password  (-u <token>:)
    # → response.traits.subtext_url
    ```

    `include=subtext_url` returns just that trait (default page size is 10 traits; `?limit=200` max). This is server-side only — no CORS. For event-level, moment-linked URLs, query the `subtext_url` column on the event-specific table (e.g. `segment.<source>.checkout_started`) instead — custom Track properties land as un-prefixed columns on `<source>.<event>` tables, not on `tracks`.
  </Step>

  <Step title="Hand off to Subtext">
    For each URL, call `review-open(session_url=<subtext_url>)` on the Subtext MCP, step through the drop, and return a Detect-vs-Diagnose read: the warehouse says they dropped, the session shows what they experienced.
  </Step>
</Steps>

<Note>
  `subtext_url` only round-trips if it was attached as a user trait or event property at capture time (see [Attach the session URL](#attach-the-session-url)). Match your region for the API host (`profiles.segment.com` vs `profiles.euw1.segment.com`). Event-level links from the warehouse event tables are moment-precise; user-trait links open at session start.
</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.
