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

# LaunchDarkly and Subtext: From a Flag to the Session

> Attach the Subtext session URL to the LaunchDarkly context, then jump from any flag signal — a guarded-rollout regression, a losing experiment, an error spike — straight into the user's real session.

LaunchDarkly tells you *that* a flag variation moved a metric: a guarded rollout is regressing, an experiment's treatment lost, an error rate climbed right after a flag change. It can't show you what a user who got that variation actually saw. Subtext can. Attach the Subtext session URL to your LaunchDarkly context and every flag 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. LaunchDarkly detects; Subtext diagnoses.

<Note>
  This guide assumes the [Subtext capture snippet](/docs/install/overview) is installed and capturing sessions, and the LaunchDarkly JS client SDK (`@launchdarkly/js-client-sdk` in v4.x; published as `launchdarkly-js-client-sdk` in earlier versions) is initialized.
</Note>

## Attach the session URL

Store the current Subtext session URL on your LaunchDarkly context so it travels with the flag evaluation and every experiment result. There are two places to attach it, depending on how precise the link needs to be.

### On the LaunchDarkly context

Attach the URL as a custom attribute on the context with `ldClient.identify`. Call it wherever your app already identifies the user — on auth resolution, not at module top level. The attribute is stored on the context and visible in the LaunchDarkly **Contexts** dashboard.

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

On the legacy user schema (pre-v3 contexts), nest the value under `custom`: `ldClient.identify({ key: user.id, custom: { subtext_url: subtextUrl } })`. Don't mark `subtext_url` as a private attribute (`privateAttributes` in your SDK config): private attributes are still used for targeting but are redacted from events, so LaunchDarkly won't store the value and it won't be readable from the Contexts page, the REST API, or the MCP tools.

### On a specific event

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

```js theme={null}
ldClient.track('checkout_failed', { 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. Pass the full context to `ldClient.identify`: it replaces the context rather than merging, so dropping existing attributes clears them. Don't use the session URL as the context `key` — keep the key stable and put the URL in its own attribute. The URL also changes on a new session and when `FS('setIdentity')` is called with a different `uid`, so re-call `ldClient.identify` on re-identify — the context attribute is frozen until the next `identify`.
</Warning>

## From signal to session

However you first hear that a rollout is going sideways in LaunchDarkly, the path is the same: get to the `subtext_url`, then hand it to your agent for a Detect-vs-Diagnose read. The metric says the new variation is worse; the session shows whether the flagged feature rendered correctly, errored, or just changed behavior people didn't like.

<AccordionGroup>
  <Accordion title="A guarded-rollout regression alert">
    LaunchDarkly posts a guarded-rollout / release-monitoring alert to Slack: "Metric `error_rate` regressed for flag `new-checkout` — rollback recommended."

    From the alert, open the flag and identify contexts that received the regressing variation. Then go to LaunchDarkly → **Contexts**, open one, and copy `subtext_url` from the **Attributes** tab. Grab several, then hand them to your agent:

    ```text theme={null}
    LaunchDarkly's guarded rollout flagged a regression on flag `new-checkout` and is recommending
    rollback. Before I pull the trigger, here are sessions from contexts that got the new variation:
    <PASTE 3-4 subtext_urls>

    Detect-vs-Diagnose, fast: the metric says the new variation is worse — what do these users
    actually experience? Did the new checkout render and work, or is it broken? Give me a clear
    roll-back / hold-and-fix recommendation with the evidence behind it.
    ```
  </Accordion>

  <Accordion title="An experiment results email">
    LaunchDarkly emails you that an experiment reached significance — and the treatment lost on a primary metric.

    Open the experiment, pull contexts in the losing variation, and copy `subtext_url` for a few from **Contexts**.

    ```text theme={null}
    LaunchDarkly says the treatment of <experiment> lost on <metric> and it's now significant. Here
    are sessions from users who got the treatment variation:
    <PASTE subtext_urls>

    Open them and tell me whether the feature behaved correctly for these users. Is the metric loss a
    bug (broken variation) or a preference (it works, users like control better)? That changes whether
    we iterate or kill it. Point to the moments.
    ```
  </Accordion>

  <Accordion title="On-call: a flag suspected in an error spike">
    Errors spike right after a flag change; you suspect the flag and want proof before reverting.

    Cross-reference the erroring users to LaunchDarkly contexts, then copy `subtext_url` from **Contexts** → **Attributes**.

    ```text theme={null}
    Errors spiked right after we moved flag `<key>`. Here's a session from a user on the new
    variation who hit trouble: <PASTE subtext_url>

    Confirm or rule out the flag: does the new variation's code path break for this user? Show me the
    UI and console at the failure. I need to decide in the next few minutes whether to revert the flag.
    ```
  </Accordion>
</AccordionGroup>

<Tip>
  Paste the agent's verdict into the flag's comments or the incident channel so the rollout decision is documented with evidence. For a rollback, ask the agent to capture a quick proof document (`doc-create`) with before/after screenshots of the variation, so the "why we rolled back" is permanent. A "metric regressed but the feature works" verdict means iterate; a "feature is broken" verdict means revert now — the session review is what tells the two apart while the rollout clock is ticking.
</Tip>

## For agents

An autonomous agent can run the whole loop against LaunchDarkly directly: discover the regressed variation, extract the `subtext_url`, and hand each session to the Subtext MCP. LaunchDarkly is the cleanest experimentation integration for this — the replay link is read back natively as a stored context attribute.

LaunchDarkly ships an official MCP server, hosted at `https://mcp.launchdarkly.com/mcp/launchdarkly` (remote, HTTP), with a local stdio server at `github.com/launchdarkly/mcp-server` for federal or EU environments. The hosted server authenticates with OAuth via its install page; the local stdio server authenticates with an API access token created on the Authorization page. See [LaunchDarkly's MCP docs](https://launchdarkly.com/docs/home/getting-started/mcp). It exposes around 100 tools — the relevant ones here are `get-experiment-results`, `get-experiment-metric-results`, `list-experiments`, and `get-experiment` for experiments, and `query-flag-evaluations` to map affected sessions to the variation they received. To read `subtext_url` back, use `search-contexts` or `get-context-instances`, which return stored context attributes.

<Steps>
  <Step title="Discover the regressed variation">
    Read experiment results with `get-experiment-results` / `get-experiment-metric-results` (mirrored by the `GET /api/v2/.../experiments...` REST endpoint) to find the variation that regressed, and use `query-flag-evaluations` to map affected sessions and contexts to the variation they received.
  </Step>

  <Step title="Extract the session URL">
    Read `subtext_url` from the stored context attributes with the MCP tools: `search-contexts` to find the context by kind, key, or attribute value, then `get-context-instances` for its full attribute set. On the local stdio server, which lacks context tools, fall back to REST: discover instances first via `POST` Search context instances or the context-instances list (`filter` e.g. `applicationId`, `sort=ts` / `-ts`, pagination), then GET the instance:

    ```
    GET /api/v2/projects/{projectKey}/environments/{environmentKey}/context-instances/{id}
    # Authorization: <apiKey>
    # subtext_url is a custom attribute inside the context object:
    #   context.<kind>.subtext_url
    ```
  </Step>

  <Step title="Hand off to Subtext">
    For each URL, call `review-open(session_url=<subtext_url>)` on the Subtext MCP, then step through the key moment in the variation for a Detect-vs-Diagnose read: metric regressed vs. did the variation actually break.
  </Step>
</Steps>

<Note>
  `subtext_url` only round-trips if it was set as a context attribute at capture time (see [Attach the session URL](#attach-the-session-url)), and the value is frozen at set-time. The documented context model nests custom attributes — including `subtext_url` — inside `context`, but the docs' example response shows `"context": null`, so confirm the exact populated nesting against a live call. Use the local stdio server for EU or federal environments where the hosted endpoint isn't permitted — note that it exposes only a small flag and AI Config toolset (feature flags, AI Configs, audit log, code references, environments), not the experiment or flag-evaluation tools above, so in those environments run the whole discovery and extraction loop over the REST API instead.
</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.
