Skip to main content

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.

note

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.

note

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
}
Why two gates?

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.

Fail-safe

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:

  1. Initializes the window.autopilot and window.autopilotOptions globals.
  2. Detects browser modernity and picks the matching bundle (ES module vs. classic script).
  3. Resolves the bundle base URL by environment (local / dev / stage / prod).
  4. Appends an async, crossorigin="anonymous" <script> tag for the Autopilot bundle, guarding against double-injection.
  5. 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_id
  • device_id
  • tab_id
  • app
  • version
  • country_code
  • user_id

This is what ties a recording back to the same session as the rest of your Pulse analytics.

Modes

ModeWhen usedSession context source
standardTop-level frame (default)window.pulse.getState()
iframeCross-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

RequirementWhere it is checkedDefault
autopilotTracking.enabled === true (resolved config: server segment → URL ?autopilot= → client → localStorage; a client false opts out)Pulse all-in-one bundlefalse
Not a web-to-mobile sessionPulse all-in-one bundle
Page has loadedPulse all-in-one bundle
Remote autopilot/autopilot_configtracking_enabled === truePulse all-in-one bundlenot loaded if missing/failed
No existing Autopilot script already injectedAutopilot loader