Initialization & Enablement
Autopilot is never initialized directly by your application. It is loaded by the Pulse SDK during Pulse's own startup, and only when two independent gates are both satisfied. This page explains the full flow and how to control whether recording happens.
Overview
Pulse SDK initializes
│
▼
Gate 1: autopilotTracking.enabled === true ? (resolved config: server segment → URL param → client → localStorage)
│ yes
▼
not in web-to-mobile mode ?
│ yes
▼
wait for page load
│
▼
Gate 2: remote setting autopilot / autopilot_config → tracking_enabled === true ?
│ yes
▼
loadAutopilotSDK(config) → injects the Autopilot bundle <script>
│
▼
Autopilot reads window.pulse.getState() for session context and starts recording
If either gate is false, Autopilot is not loaded and no recording takes place.
Gate 1 — The autopilotTracking.enabled config flag
The first gate is a Pulse configuration flag:
// pulse-web — packages/common/src/models/configurations.ts
abstract autopilotTracking: {
enabled: boolean;
};
Its default value is false:
// pulse-web — packages/browser/shared/src/configuration.ts
autopilotTracking: {
enabled: false,
},
The flag is normally turned on server-side per segment by the Pulse loader Worker, rather than hard-coded in each application. For example, the Worker enables it only for specific segments:
// pulse-web — apps/server/src/data/values.ts
{
if: [any('tools_stage', 'tools_dev', 'pa_stage', 'pa_prod')],
then(env) {
setProperty(env, 'config.autopilotTracking', { enabled: true });
},
},
This means a build can ship with Autopilot capability while only a chosen set of segments/environments actually opts in.
Client opt-out
An integrator can override this flag from the client to keep Autopilot off, even for a segment where the server enabled it. The enablement gate is evaluated against the fully-resolved Pulse config — server segment config, then the loader URL ?autopilot= param, then any client-passed SDK config, then a localStorage override — so a client value wins over the server default:
// @pulse/browser
initialize({
app: "your application name",
autopilotTracking: { enabled: false },
});
// @pulse/react
<PulseProvider app="your application name" tracker={{ autopilotTracking: { enabled: false } }}>
{/* ... */}
</PulseProvider>
Integrators who preload the Pulse loader script can also opt out straight from the script URL, without touching application code:
<script src="https://optifyr.com/pulse/{{appName}}/module/pulse.js?autopilot=false" async></script>
The autopilot query param is resolved server-side into config.autopilotTracking.enabled, overriding the per-segment default.
When the resolved autopilotTracking.enabled is not true, loadAutopilotIfEnabled returns immediately and the Autopilot bundle is never requested. The full integrator-facing guide — the SDK config, the URL query param, the localStorage channel, and the precedence between them — lives in Web SDK → Disabling Autopilot Session Recording.
Setting enabled: false is a guaranteed opt-out. Setting enabled: true from the client is not sufficient to start recording on its own — Gate 2 (the remote setting) must still allow it.
Web-to-mobile sessions are explicitly excluded. If webToMobileService.getWebToMobileMetaInfo() returns a value, Autopilot is not loaded even when the flag is on.
Gate 2 — The autopilot_config remote setting
Even when the config flag is on, Pulse makes a runtime check against a remote setting before loading Autopilot. After the page has loaded, Pulse fetches the setting and reads its tracking_enabled field:
// pulse-web — packages/browser/loader/src/bundles/all-in-one.bundle.ts
const AUTOPILOT_SETTING_TAG = 'autopilot';
const AUTOPILOT_CONFIG_SETTING_NAME = 'autopilot_config';
interface AutopilotRemoteConfig {
tracking_enabled?: boolean;
}
async function loadAutopilotIfEnabled(pageLoadedService, settings, webToMobileService, config) {
if (
config?.autopilotTracking?.enabled !== true ||
webToMobileService.getWebToMobileMetaInfo()
) {
return;
}
await firstValueFrom(pageLoadedService.whenLoaded());
try {
const autopilotConfig = await settings.get<AutopilotRemoteConfig | undefined>(
AUTOPILOT_SETTING_TAG,
AUTOPILOT_CONFIG_SETTING_NAME,
);
if (autopilotConfig?.tracking_enabled === true) {
loadAutopilotSDK(config);
}
} catch {
// Setting unavailable or request failed — do not load Autopilot.
}
}
The remote setting lives under the autopilot tag with name autopilot_config and shape:
{
"tracking_enabled": true
}
The config flag is decided at load time (build/segment level), while the remote setting can be flipped on or off at any time without a new deployment. The remote setting acts as a runtime kill-switch: set tracking_enabled to false and recording stops for all clients on their next page load, even where the config flag is on.
If the remote setting request fails or the setting is missing, the catch block ensures Autopilot is not loaded. The safe default is "do not record".
Loading the Autopilot bundle
Once both gates pass, loadAutopilotSDK(config) injects the Autopilot bundle. The loader:
- Initializes the
window.autopilotandwindow.autopilotOptionsglobals. - Detects browser modernity and picks the matching bundle (ES module vs. classic script).
- Resolves the bundle base URL by environment (local / dev / stage / prod).
- Appends an
async,crossorigin="anonymous"<script>tag for the Autopilot bundle, guarding against double-injection. - Seeds the
window.autopilot.__internals (queue, config, base URL, timestamp, modernity flag) that the bundle reads on startup.
// pulse-web — packages/browser/loader/src/bundles/autopilot.loader.ts
export function loadAutopilotSDK(
config: Partial<PulseConfigurations>,
mode: AutopilotMode = 'standard',
initialFrameState?: AutopilotFrameState,
): void {
(window as any).autopilot = (window as any).autopilot || {};
(window as any).autopilotOptions = (window as any).autopilotOptions || {};
if (
((mode === 'iframe' && initialFrameState) ||
(config?.autopilotTracking?.enabled === true && mode === 'standard')) &&
!scriptAlreadyExists()
) {
addScript(config, mode, initialFrameState);
}
}
Session context from Pulse
After the bundle loads, Autopilot pulls its session context from Pulse rather than generating its own. In standard mode it calls window.pulse.getState() to obtain:
session_iddevice_idtab_idappversioncountry_codeuser_id
This is what ties a recording back to the same session as the rest of your Pulse analytics.
Modes
| Mode | When used | Session context source |
|---|---|---|
standard | Top-level frame (default) | window.pulse.getState() |
iframe | Cross-frame recording inside an <iframe> | Frame state delivered by the parent frame via the autopilot/set-frame-id postMessage |
In iframe mode the parent frame (running Autopilot in standard mode) sends the iframe its frame state, and the iframe initializes from that payload instead of calling Pulse directly.
Summary
| Requirement | Where it is checked | Default |
|---|---|---|
autopilotTracking.enabled === true (resolved config: server segment → URL ?autopilot= → client → localStorage; a client false opts out) | Pulse all-in-one bundle | false |
| Not a web-to-mobile session | Pulse all-in-one bundle | — |
| Page has loaded | Pulse all-in-one bundle | — |
Remote autopilot/autopilot_config → tracking_enabled === true | Pulse all-in-one bundle | not loaded if missing/failed |
| No existing Autopilot script already injected | Autopilot loader | — |