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

# Datadog and Subtext: From a Signal to the Session

> Attach the Subtext session URL to Datadog RUM, then jump from any Datadog signal — a tripped monitor, a RUM error, an incident dashboard — straight into the user's real session.

Datadog tells you *that* something crossed a line: a monitor tripped, a RUM error rate spiked, failed requests climbed on a dashboard. It can't tell you *why* one specific person was blocked. Subtext can. Attach the Subtext session URL to your Datadog RUM data and every Datadog 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. Datadog detects; Subtext diagnoses.

<Note>
  This guide assumes the [Subtext capture snippet](/docs/install/overview) is installed and the Datadog Browser RUM SDK (`@datadog/browser-rum`) is initialized.
</Note>

## Attach the session URL

Store the current Subtext session URL on your Datadog RUM 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 context

Attach the URL to the RUM user context via `setUser`. It then rides along on every subsequent view, action, error, and resource event as `@usr.subtext_url`. 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) {
    datadogRum.setUser({ id: 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) {
      datadogRum.setUser({ id: user.id, email: user.email, subtext_url: subtextUrl })
    }
  }, [user])
  ```
</CodeGroup>

### On global context

If you prefer not to overload the user object, set it as global context instead. It lands on events as `@context.subtext_url`.

```js theme={null}
datadogRum.setGlobalContextProperty('subtext_url', subtextUrl)
```

### On a specific action

When the exact moment matters, attach the URL to a single custom action. `url.now` carries a timestamp, so the link opens the replay at that instant — ideal for high-signal moments like errors, rage clicks, and conversion failures.

```js theme={null}
datadogRum.addAction('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 Subtext session URL also changes on a new session and when `FS('setIdentity')` is called with a different `uid`, so re-call `setUser` (or `setGlobalContextProperty`) whenever you re-identify the user; the value is frozen at set-time and does not update automatically. `setUser` replaces the whole user object while `setUserProperty` merges — pick one and stay consistent. And don't stash the URL only in `addError` extra context; set it on the user or global context so it's searchable across every RUM event type.
</Warning>

## From signal to session

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

<AccordionGroup>
  <Accordion title="A PagerDuty page at 2am">
    A Datadog monitor trips and PagerDuty pages the on-call engineer: "Browser RUM error rate > 5% on `/checkout` for 5 min."

    Open the monitor from the page and jump to the correlated RUM events. Open a representative error event, go to **Attributes**, and copy `usr.subtext_url`.

    ```text theme={null}
    A Datadog monitor paged me: Browser RUM 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="A Slack alert on a frontend error">
    Datadog posts to your alerts channel: "RUM error: `Uncaught TypeError` — 60 sessions in the last hour."

    Click into the RUM Explorer from the alert, filter to the error, open a few events, and copy `usr.subtext_url` from several of them.

    ```text theme={null}
    Datadog flagged a recurring RUM 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="An incident review from a dashboard">
    During or after an incident, a Datadog dashboard shows a clear anomaly window: a spike in failed requests correlated with a drop in conversions.

    In the RUM Explorer, scope to the incident window, filter `@usr.subtext_url:*`, and open a session that sits inside the spike. Copy `usr.subtext_url`.

    ```text theme={null}
    We had an incident between <start> and <end>. This Datadog dashboard shows failed requests
    spiking against a conversion drop. Here's a user session from inside that window: <PASTE subtext_url>

    Open it and tell me the human story behind the graph — what did this user try to do, where did it
    fail, and what did the failure look like on screen? I'm writing the postmortem; I need the
    customer-impact narrative, not just the metrics.
    ```
  </Accordion>
</AccordionGroup>

<Tip>
  For a postmortem, ask your agent to assemble a proof document (`doc-create`) with the key screenshots and the session link — one URL that shows what users actually hit — then drop it into the incident channel so the metrics graph gets a human-impact caption. And if the session proves the alert was noise (error fired, user unaffected), feed that Detect-vs-Diagnose "no real impact" verdict back into the monitor's tuning.
</Tip>

## For agents

An autonomous agent can run the whole loop against Datadog directly: discover the friction, extract the `subtext_url`, and hand each session to the Subtext MCP. Datadog ships a first-party MCP server (GA March 2026). The hosted endpoint is site-parametrized (US1 form `https://mcp.datadoghq.com/api/unstable/mcp-server/mcp` — check your site's setup docs for the current stable URL), with local stdio via the `datadog_mcp_cli` binary. Auth is OAuth 2.0, with `DD_API_KEY` + `DD_APPLICATION_KEY` headers (or a bearer token in the `Authorization` header) as a fallback. Docs: `docs.datadoghq.com/mcp_server/`. Repo: `github.com/datadog-labs/mcp-server`.

<Steps>
  <Step title="Discover who is affected">
    `search_datadog_rum_events` is the core discovery tool — filter on the frustration facet `@action.frustration.type` (values `rage_click`, `dead_click`, `error_click`) to surface hotspots, or `@session.frustration.count:>1` for session-level friction. For grouped errors, use `search_datadog_error_tracking_issues` and `get_datadog_error_tracking_issue`; `ddsql_run_query` runs SQL over the `dd.rum` table function (pick the event subtype via its `event_type` parameter), and `get_rum_summary` / `get_rum_insight` return summarized hotspots.

    Without the MCP, the same over REST:

    ```
    POST /api/v2/rum/events/search          # raw events, filter.query "@type:error"
    POST /api/v2/rum/analytics/aggregate    # group_by @action.frustration.type for counts
    POST /api/v2/error-tracking/issues/search   # grouped errors
    # DD-API-KEY / DD-APPLICATION-KEY headers
    ```
  </Step>

  <Step title="Extract the session URL">
    Filter the same RUM search on `@usr.subtext_url:*` to pull the replay links — `search_datadog_rum_events` does both jobs. Over REST:

    ```
    POST /api/v2/rum/events/search
    # body: { "filter": { "query": "@usr.subtext_url:*" } }
    # response path: data[N].attributes.attributes["usr.subtext_url"]
    ```

    If you set the URL as global context instead of on the user, it lands as `@context.subtext_url`.
  </Step>

  <Step title="Hand off to Subtext">
    For each URL, call `review-open(session_url=<subtext_url>)` on the Subtext MCP. Action-level URLs are moment-precise, so the review opens exactly when the signal fired. For an incident, scope the RUM search 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)); the value is frozen at set-time and doesn't auto-update. Action-level links are moment-precise; user- and global-context links open at the moment the user was identified. Check a live response for the exact nesting of `usr.subtext_url` under `data[N].attributes.attributes`. Match your Datadog site (US1/US3/US5/EU/AP1/AP2/UK1) for both the MCP endpoint and the API 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.
