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

# New Relic and Subtext: From a Signal to the Session

> Attach the Subtext session URL to New Relic browser events, then jump from any New Relic signal — an open issue, an Errors Inbox group, a rage click — straight into the user's real session.

New Relic tells you *that* something crossed a line: an issue opened, an error group grew, rage clicks climbed on a dashboard. It can't tell you *why* one specific person was blocked. Subtext can. Attach the Subtext session URL to your browser events and every New Relic 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. New Relic detects; Subtext diagnoses.

<Note>
  This guide assumes the [Subtext capture snippet](/docs/install/overview) is installed and the New Relic browser agent is running — either the copy-paste snippet or the `@newrelic/browser-agent` npm package. Both expose the same `newrelic` global, so every call below works unchanged.
</Note>

## Attach the session URL

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

### On every browser event

`setCustomAttribute` adds a name/value pair to subsequent events on the page. Pass `true` as the third argument and the pair is also written to browser storage, so later same-origin page loads in the same session re-apply it automatically. 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) {
    newrelic.setCustomAttribute('subtext_url', subtextUrl, true)
    newrelic.setUserId(user.id)
  }
  ```

  ```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) {
      newrelic.setCustomAttribute('subtext_url', subtextUrl, true)
      newrelic.setUserId(user.id)
    }
  }, [user])
  ```
</CodeGroup>

The attribute lands on `AjaxRequest`, `BrowserInteraction`, `BrowserPerformance`, `JavaScriptError`, `Log`, `PageAction`, `PageView`, `PageViewTiming`, and `UserAction` — so a single call makes the session URL queryable from almost anywhere in NRQL.

`setUserId` is worth pairing with it. It writes `enduser.id` on every event, persists across page loads on its own, and is attached to `JavaScriptError` specifically so Errors Inbox can group by user.

### On a specific action

When the exact moment matters, attach the URL to a `PageAction`. `url.now` carries a timestamp, so the link opens the replay at that instant — ideal for high-signal moments like checkout failures and conversion drop-offs.

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

### On a handled error

For an error you catch yourself, pass the URL as a custom attribute on `noticeError`. It lands on the `JavaScriptError` event alongside the stack trace.

```js theme={null}
try {
  await submitPayment(payload)
} catch (err) {
  newrelic.noticeError(err, { 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. Custom attribute values must be simple types: string, number, boolean, or `null`. A session URL is a string, so it fits, but passing an object silently fails. To land the attribute on the `PageView` event specifically, set it before the window load event fires; that's when `PageView` is transmitted. Avoid reserved NRQL words for attribute names. The `persist` argument needs browser agent v1.230.0 or higher.
</Warning>

## From signal to session

However you first hear about a problem in New Relic, the path is the same: get to the `subtext_url`, then hand it to your agent for a Detect-vs-Diagnose read. New Relic tells you a threshold broke or an error group grew; the session tells you whether a real person was actually blocked, what they saw, and the precise sequence behind it.

<AccordionGroup>
  <Accordion title="A page at 2am from an open issue">
    An alert condition trips, New Relic opens an issue, and your workflow pages the on-call engineer: "Browser JS error rate above threshold on `/checkout`."

    Open the issue, jump to the correlated events, and copy `subtext_url` from a representative one. Or run it directly in the query builder:

    ```sql theme={null}
    FROM JavaScriptError
    SELECT subtext_url, `enduser.id`, errorMessage, pageUrl
    WHERE pageUrl LIKE '%/checkout%' AND subtext_url IS NOT NULL
    SINCE 30 minutes ago LIMIT 10
    ```

    ```text theme={null}
    A New Relic issue paged me: browser JS error rate spiking on /checkout. Here's a session from
    an affected user: <paste subtext_url>

    It's 2am — I need signal fast. Open the session, give me a Detect-vs-Diagnose read. Is this a
    real user-facing outage or noisy telemetry? What broke, what did the user see, and is it isolated
    or systemic? One paragraph, plus the single most important screenshot.
    ```
  </Accordion>

  <Accordion title="An error group in Errors Inbox">
    Errors Inbox groups a recurring `Uncaught TypeError` across dozens of sessions and assigns it to you.

    Open the group and read `subtext_url` off several events — because you set `enduser.id`, you can also see how many distinct people hit it, not just how many events fired.

    ```sql theme={null}
    FROM JavaScriptError
    SELECT uniqueCount(`enduser.id`), latest(subtext_url)
    WHERE errorMessage LIKE '%Cannot read properties%'
    FACET errorMessage SINCE 1 hour ago
    ```

    ```text theme={null}
    New Relic Errors Inbox flagged a recurring error across ~60 sessions. Here are subtext_urls from
    a sample: <paste 3–4 subtext_urls>

    Review them together. What's the common trigger — same component, same API failure, same device?
    Which sessions show the user actually stuck vs. recovered? Give me one root-cause hypothesis and a
    go/no-go on whether this needs a hotfix tonight.
    ```
  </Accordion>

  <Accordion title="A rage-click sweep with no alert attached">
    Nothing paged you. You want to find the friction New Relic noticed but nobody thresholded.

    The browser agent classifies user actions on its own. `rageClick`, `deadClick`, and `errorClick` are plain boolean attributes on `UserAction`, so the sweep is one query:

    ```sql theme={null}
    FROM UserAction
    SELECT session, subtext_url, currentUrl, actionName, deviceType
    WHERE (rageClick IS true OR deadClick IS true OR errorClick IS true)
      AND subtext_url IS NOT NULL
    SINCE 1 day ago LIMIT 50
    ```

    ```text theme={null}
    New Relic flagged rage and dead clicks across these sessions: <paste 3–4 subtext_urls>

    Open each one and tell me what the user was trying to do at the moment they started
    clicking repeatedly. Is the control broken, mislabeled, or just slow? Rank the findings by how
    many people this would affect.
    ```
  </Accordion>
</AccordionGroup>

<Tip>
  New Relic session replay and Subtext do different jobs. Filter on `hasReplay IS true AND subtext_url IS NOT NULL` and you get sessions with both: a New Relic replay a person can watch, and a Subtext session your agent can read. For a postmortem, ask your agent to assemble a proof document (`doc-create`) with the key screenshots and the session link, then drop it into the incident channel so the metrics chart gets a human-impact caption.
</Tip>

## For agents

An autonomous agent can run the whole loop against New Relic directly: discover the friction, extract the `subtext_url`, and hand each session to the Subtext MCP. New Relic hosts a first-party MCP server over Streamable HTTP at `https://mcp.newrelic.com/mcp/` (US), `https://mcp.eu.newrelic.com/mcp/` (EU), and `https://mcp.jp.newrelic.com/mcp/` (JP). Authenticate with a user API key in an `api-key` header (`NRAK-…` format) or with OAuth. Clients that need stdio can bridge with `npx mcp-remote`.

<Note>
  New Relic's MCP server accepts an `include-tags` header that filters which tools the agent sees. The available tags are `discovery`, `data-access`, `alerting`, `incident-response`, `performance-analytics`, and `advanced-analysis`. For the Subtext loop, `include-tags: data-access,incident-response` is enough — it trims the tool corpus to the ones below and leaves more room for the review itself.
</Note>

<Steps>
  <Step title="Discover who is affected">
    `execute_nrql_query` is the core discovery tool — everything in the section above is one NRQL string. For grouped errors, `list_entity_error_groups` pulls Errors Inbox groups for an entity within a time window. `list_recent_issues` and `search_incident` cover open issues and alert events. `generate_user_impact_report` produces an end-user impact analysis for a specific issue, which is a natural place to hand off. `natural_language_to_nrql_query` writes and runs the query for you when you'd rather describe the question than compose NRQL.

    Without the MCP, the same over NerdGraph:

    ```graphql theme={null}
    {
      actor {
        account(id: YOUR_ACCOUNT_ID) {
          nrql(query: "FROM UserAction SELECT subtext_url WHERE rageClick IS true SINCE 1 day ago") {
            results
          }
        }
      }
    }
    ```

    ```
    POST https://api.newrelic.com/graphql
    # API-Key: NRAK-...   (api.eu.newrelic.com / api.jp.newrelic.com for other regions)
    ```
  </Step>

  <Step title="Extract the session URL">
    Because `setCustomAttribute` writes to every browser event type, one `SELECT subtext_url` does the job on whichever event carries the signal — `JavaScriptError` for errors, `UserAction` for friction, `PageAction` for the moments you instrumented yourself. Add `WHERE subtext_url IS NOT NULL` to skip sessions captured before the attribute was attached; inverting it to `IS NULL` surfaces your capture gaps.
  </Step>

  <Step title="Hand off to Subtext">
    For each URL, call `review-open(session_url=<subtext_url>)` on the Subtext MCP. `PageAction` and `noticeError` links are moment-precise, so the review opens exactly when the signal fired. For an incident, scope the NRQL to the incident window first, then review a session from inside the spike.
  </Step>
</Steps>

<Note>
  `subtext_url` only round-trips if it was attached at capture time (see [Attach the session URL](#attach-the-session-url)). Links set through `addPageAction` or `noticeError` are moment-precise; the persisted custom attribute opens at the moment the user was identified. `UserAction` events and the `rageClick` / `deadClick` / `errorClick` attributes require the Pro or Pro+SPA browser agent at v1.268.0 or higher — they are not reported by the Lite agent. Match your New Relic region (US/EU/JP) for the MCP endpoint and the NerdGraph host.
</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.
