> ## Documentation Index
> Fetch the complete documentation index at: https://jam.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect your domain

> Connect your own domain to Recording Links so console logs and network requests get captured from every customer recording.

Connecting your domain ties your website or web app to Jam so Recording Links capture console logs, network requests, and user interactions alongside the screen recording. Without a connected domain, screen recordings still work, but developer logs aren't included.

A connected domain lets Jam:

* Serve Recording Links from your own domain, so links look branded and familiar to your customers.
* Capture console logs and network requests on every recording.
* Attach developer logs directly to Jams.
* Auto-blur sensitive fields like passwords and payment details while recording.

You need write access to your site to complete the steps below.

<Note>
  Every snippet on this page has a runnable example in the [recording-links-demos](https://github.com/jamdotdev/recording-links-demos) repo, including HTML, Next.js, Angular, and Google Tag Manager.
</Note>

## Setup

<Steps>
  <Step title="Install the code snippets">
    Open [**Settings → Jam SDK**](https://jam.dev/s/settings/sdk) and pick your framework. The snippet there has your Team ID filled in, and the picker keeps your selection in the address bar (for example, `?framework=angular`), so you can share a link that opens on the right snippet.

    <Frame>
      <img src="https://mintcdn.com/jam-1eb4fd26/9epf29xrXj8V0UiJ/images/connect-domain-framework-picker.png?fit=max&auto=format&n=9epf29xrXj8V0UiJ&q=85&s=239950073a4819b156f3067318ecaa80" alt="Connect Domain step with framework options HTML, Next.js, Angular, GTM, and Other" width="1320" height="360" data-path="images/connect-domain-framework-picker.png" />
    </Frame>

    <Tip>
      Next to each snippet, click **Copy agent prompt** to get an install instruction with your Team ID and snippet filled in. Paste it into an AI coding agent like Claude Code or Cursor, and the agent installs the scripts for you.
    </Tip>

    Jam's Recorder and Capture scripts must be served from the **same origin** as your site so recordings and logs are correctly associated. Find your Team ID in [**Settings → Jam SDK**](https://jam.dev/s/settings/sdk), then install the snippet for your framework on all pages, or at minimum on the URL that hosts your recording route.

    <Tabs>
      <Tab title="HTML">
        Paste this into the `<head>` of every page.

        ```html theme={"theme":"css-variables"}
        <meta name="jam:team" content="your-team-id" />
        <script type="module" src="https://js.jam.dev/recorder.js"></script>
        <script type="module" src="https://js.jam.dev/capture.js"></script>
        ```
      </Tab>

      <Tab title="Next.js">
        Use `next/script` with `strategy="beforeInteractive"`. Place the snippet in the **root** layout, not a nested route layout, since `beforeInteractive` is only honored from the root.

        <CodeGroup>
          ```tsx app/layout.tsx (App Router) theme={"theme":"css-variables"}
          import Script from "next/script";

          export default function RootLayout({ children }: { children: React.ReactNode }) {
            return (
              <html lang="en">
                <head>
                  <meta name="jam:team" content="your-team-id" />
                </head>
                <body>
                  <Script
                    src="https://js.jam.dev/recorder.js"
                    type="module"
                    strategy="beforeInteractive"
                  />
                  <Script
                    src="https://js.jam.dev/capture.js"
                    type="module"
                    strategy="beforeInteractive"
                  />
                  {children}
                </body>
              </html>
            );
          }
          ```

          ```tsx pages/_document.tsx (Pages Router) theme={"theme":"css-variables"}
          import { Html, Head, Main, NextScript } from "next/document";
          import Script from "next/script";

          export default function Document() {
            return (
              <Html lang="en">
                <Head>
                  <meta name="jam:team" content="your-team-id" />
                </Head>
                <body>
                  <Script
                    src="https://js.jam.dev/recorder.js"
                    type="module"
                    strategy="beforeInteractive"
                  />
                  <Script
                    src="https://js.jam.dev/capture.js"
                    type="module"
                    strategy="beforeInteractive"
                  />
                  <Main />
                  <NextScript />
                </body>
              </Html>
            );
          }
          ```
        </CodeGroup>
      </Tab>

      <Tab title="Angular">
        Install the package, then initialize it once from your root component. Wrap the call in `ngZone.runOutsideAngular` so the SDK's listeners don't trigger change detection.

        ```bash theme={"theme":"css-variables"}
        npm install @jam.dev/recording-links
        ```

        ```ts theme={"theme":"css-variables"}
        import { Component, OnInit, NgZone } from '@angular/core';
        import * as jam from '@jam.dev/recording-links/sdk';

        @Component({ selector: 'app-root', template: '<router-outlet></router-outlet>' })
        export class AppComponent implements OnInit {
          constructor(private ngZone: NgZone) {}
          ngOnInit() {
            this.ngZone.runOutsideAngular(() => {
              jam.initialize({ teamId: 'your-team-id' });
            });
          }
        }
        ```

        With the SDK, the recorder loads lazily. The recorder and capture scripts download only when someone opens a page through a Recording Link, so `window.jam` won't exist on a normal page view.
      </Tab>

      <Tab title="Google Tag Manager">
        Create a **Custom HTML** tag that fires on **All Pages**, and paste this in. It loads the same Recorder and Capture scripts as the other methods, with a guard so it runs once per page.

        ```html theme={"theme":"css-variables"}
        <meta name="jam:team" content="your-team-id">
        <script>
        (function () {
          if (window.jam) return;
          var sources = [
            "https://js.jam.dev/recorder.js",
            "https://js.jam.dev/capture.js"
          ];
          for (var i = 0; i < sources.length; i++) {
            var script = document.createElement("script");
            script.type = "module";
            script.src = sources[i];
            document.head.appendChild(script);
          }
        })();
        </script>
        ```

        <Note>
          GTM compiles Custom HTML tags as ES5, so it rejects `async`/`await` and dynamic `import()` with errors like *"async function requires ECMASCRIPT\_2017."* This snippet stays ES5 and injects the scripts as DOM elements, which avoids those errors.
        </Note>
      </Tab>
    </Tabs>

    **What each script does:**

    * **`recorder.js`** shows the recording interface when a Recording Link is opened, and lets users record directly from your site.
    * **`capture.js`** captures console logs, network requests, and click and key interactions while a recording is in progress.

    To serve one page to more than one workspace, add a `jam:team` meta tag per workspace.

    <Warning>
      Wait for both scripts to load successfully on your target URL before verifying your domain. Check that your Content Security Policy isn't blocking them.
    </Warning>

    **Performance note:** Jam caches assets aggressively to reduce load. For the plain HTML snippet, place the `<script>` tags as early as possible in `<head>`. Loading them with `async`, `defer`, or lazy `import()` can skip early logs and requests. The Next.js `beforeInteractive` strategy and the SDK handle this for you.
  </Step>

  <Step title="Update your Content Security Policy (if applicable)">
    If your site doesn't define any Content Security Policy directives, skip this step.

    If your site sets a CSP with `frame-src` or `script-src`, add `*.jam.dev` to both. Otherwise the Jam scripts are blocked.

    ```html theme={"theme":"css-variables"}
    <meta
      http-equiv="Content-Security-Policy"
      content="frame-src 'self' *.jam.dev; script-src 'self' *.jam.dev;"
    />
    ```
  </Step>

  <Step title="Verify your domain">
    After installing the scripts, verify your domain so Jam confirms they load and run correctly.

    1. Go to [**Settings → Jam SDK**](https://jam.dev/s/settings/sdk).
    2. Under **Verify domain**, paste the URL where the scripts are installed (for example, `example.com` or `example.com/recorder`).
    3. Click **Verify**.

    Jam opens your URL and checks that:

    * The page is publicly accessible.
    * The URL doesn't redirect.
    * Query parameters are preserved.
    * Both the Recorder and Capture scripts are installed with a matching Team ID.

    On success, Jam confirms with a **Domain successfully verified** toast, the domain appears under **Connect Domain** marked **Installed**, and Recording Links from that domain capture console logs and network requests.

    <Warning>
      If your app redirects users from the recording URL (for example, sending unauthenticated users to a login page) keep all `jam-` query parameters through the redirect. The most important is `jam-recording=...`. Open your recording URL in an incognito window before verifying to check for unexpected redirects or parameter stripping.
    </Warning>
  </Step>
</Steps>

## Check your connection status

Jam shows your connection status in two places, so you can tell at a glance whether a recording will capture logs:

* **The Recording Links header**: the globe button carries a status dot. Red means no domain is connected yet, and clicking it starts setup. Green means at least one domain is connected, and hovering shows which one.
* **The domain picker when you create a Recording Link**: each domain under **Start recording from** shows a dot. Green means **Log capture enabled** (the domain is verified). Red means **Log capture disabled**. The default `recorder.jam.dev` records fine, but can't capture your site's logs, so it always shows red. **Connect your domain** at the bottom of the dropdown starts setup for another domain.

## Connect more than one domain

Once your first domain is verified, open [**Settings → Jam SDK**](https://jam.dev/s/settings/sdk) and click **Add another domain** to connect the next site to the same workspace. This opens the same install and verify steps. Each connected domain appears in the list marked **Installed**, and shows up in the domain picker when you create a Recording Link.

## Domain and subdomain behavior

In most cases, a recorder installed on the root domain (`example.com`) can capture events from subdomains (`sub.example.com`), and vice versa.

**Safari limitation:** In Safari, logs are only captured when the recorder and capture scripts run on the exact same subdomain. If Safari support is important for your team, install the recorder on the same subdomain where your users are active.

## Current limitations

| Limitation                | Details                                                                                                                                                             |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Browser support**       | Fully supported in Chrome (including Incognito) and Firefox (including Private Windows). Supported in most Safari windows. Not supported in Safari Private Windows. |
| **Iframes**               | If the Capture script is installed inside an iframe, top-level page logs are not captured.                                                                          |
| **Script loading**        | Scripts loaded with `async`, `defer`, or lazy `import()` may miss early console logs and network requests.                                                          |
| **Verification required** | Installing scripts alone is not enough. You must verify your domain in [**Settings → Jam SDK**](https://jam.dev/s/settings/sdk).                                    |

## FAQ

<AccordionGroup>
  <Accordion title="Can I put the Recorder and Capture scripts on different pages?">
    Yes. The `<meta name="jam:team" />` tag must be present on any page where `recorder.js` is installed. It's optional on pages that only include `capture.js`. Logs are only captured from pages where the Capture script is running.
  </Accordion>

  <Accordion title="Can I programmatically create Recording Links?">
    Not yet. Jam plans to expose an API for creating Recording Links programmatically (for example, from Slack, Zendesk, or directly within your app).
  </Accordion>

  <Accordion title="Can I customize the recorder UI?">
    Not currently. Contact Jam if you have specific customization requirements.
  </Accordion>

  <Accordion title="Can I mix Jam recordings with my own recording infrastructure?">
    No. Jam's Recorder and Capture scripts must be used together. Due to browser storage and cross-origin restrictions, partial integrations or mixing with external recording systems are not supported.
  </Accordion>
</AccordionGroup>

## If verification fails

When Jam can't find the scripts on your URL, verification stops with **Scripts not detected. Ensure both code snippets are installed here and try again.** Common causes:

* Team ID mismatch
* Scripts not installed on the target URL
* Scripts blocked by your CSP
* URL requires authentication
* URL redirects before scripts load
* Query parameters stripped during redirect
* Recorder and Capture scripts on different domains

Fix the issue and retry verification.
