Connect your domain - Jam Documenation
Connecting Your Domain
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.
Every snippet on this page has a runnable example in the recording-links-demos repo, including HTML, Next.js, Angular, and Google Tag Manager.
Setup
- Install the Code Snippets
Open Settings → Jam 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.
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.
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, then install the snippet for your framework on all pages, or at minimum on the URL that hosts your recording route.
- HTML
- Next.js
- Angular
- Google Tag Manager
Paste this into the <head> of every page.
<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>
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.
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>
);
}
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>
);
}
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.
npm install @jam.dev/recording-links
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.
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.
<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>
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.
What Each Script Does:
recorder.jsshows the recording interface when a Recording Link is opened, and lets users record directly from your site.capture.jscaptures 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.
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.
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.
- 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.
<meta
http-equiv="Content-Security-Policy"
content="frame-src 'self' *.jam.dev; script-src 'self' *.jam.dev;"
/>
- Verify Your Domain
After installing the scripts, verify your domain so Jam confirms they load and run correctly.
- Go to Settings → Jam SDK.
- Under Verify domain, paste the URL where the scripts are installed (for example,
example.comorexample.com/recorder). - Click Verify.
- Under Verify domain, paste the URL where the scripts are installed (for example,
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.
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.
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.devrecords 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 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. |
FAQ
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.
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).
Can I customize the recorder UI?
Not currently. Contact Jam if you have specific customization requirements.
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.
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.