Skip to main content

Journey Embed SDK

The Journey Embed SDK provides a fluent builder API for embedding epilot Journeys into any website. It unifies both embedding backends β€” Web Components and iframes β€” behind a single, chainable interface.

Use the SDK when you need programmatic control over how Journeys are embedded: building SPAs, reacting to user interactions, toggling full-screen on demand, or injecting pre-filled data at runtime.

When to use the SDK vs. raw Web Components

If you only need a static embed that never changes, the <epilot-journey> Web Component is simpler β€” just drop an element in your HTML. The SDK is the better choice when your integration is driven by JavaScript: SPAs, dynamic data injection, or triggering full-screen from a button click.

Quick Start​

1. Add the SDK script​

The SDK is a single script that exposes $epilot on window. When using .asWebComponent(), the SDK automatically loads the web component script β€” no additional <script> tags required.

Single script β€” that's all you need
<script src="https://embed.journey.epilot.io/sdk/bundle.js"></script>

When using .asIframe(), the SDK is entirely self-contained β€” no web component script is loaded.

2. Embed a Journey​

Call $epilot.embed() after the DOM is ready and chain your configuration options. End the chain with an injection method (append, prepend, before, or after) to insert the Journey into the page:

Inline embed via SDK
<div id="embed-target"></div>

<script>
document.addEventListener('DOMContentLoaded', function () {
$epilot
.embed('<your-journey-id>')
.asWebComponent()
.mode('inline')
.topBar(true)
.scrollToTop(true)
.append('#embed-target')
})
</script>

3. You're all set​

The Journey is injected into #embed-target. Read on for the full API reference and advanced scenarios.


Live Examples​

Browse interactive, runnable examples for every SDK scenario:

  • SDK Storybook β€” inline, full-screen, inline-to-fullscreen, launcher, data injection, and more β€” with both iframe and web component backends.
  • Web Component Storybook β€” the same scenarios using raw <epilot-journey> attributes (no SDK).

Use the Controls panel in Storybook to switch between backends and change options in real time. These examples work with public Journeys only β€” enter your own journey-id in the controls to see it in action.


Embed Targets​

The SDK supports two rendering backends. Choose one by calling the corresponding method in your chain:

MethodBackendNotes
.asWebComponent()<epilot-journey> custom elementRecommended. Uses Shadow DOM β€” better performance and accessibility. The web component script is auto-loaded.
.asIframe()<iframe>Useful when you need to support legacy environments or host multiple Journeys simultaneously.

Both backends accept the same configuration methods. When using .asIframe(), some options (like scrollToTop, closeButton, and contextData) are delivered to the Journey via postMessage after it signals readiness, while dataInjectionOptions is encoded in the iframe URL.

The main behavioural difference between backends appears in the Inline to Full-Screen pattern: web components require an explicit .mode() call alongside isFullScreenEntered(), while iframes do not.


API Reference​

All configuration methods are chainable and return the Embedding instance. Call an injection method at the end of the chain to render the Journey.

Configuration methods​

MethodTypeDefaultDescription
.mode(value)"inline" | "full-screen""inline"The display mode. "inline" renders within the page flow. "full-screen" renders as an overlay.
.topBar(value)booleantrueWhether to show the top navigation bar.
.scrollToTop(value)booleantrueWhether to scroll to the top of the Journey on step navigation.
.closeButton(value)booleantrueWhether to show the close button in the top bar.
.lang(value)"de" | "en" | "fr"β€”Overrides the Journey UI language. Affects labels and copy, not static Journey content.
.canary()β€”β€”Uses the canary release channel for the web component script instead of stable. See Release Channels.
.contextData(value)Record<string, unknown>β€”Additional key-value data passed to the Journey and included with the submission. See Context Data.
.dataInjectionOptions(value)DataInjectionOptionsβ€”Pre-fills Journey fields and controls the starting step. See Data Injection.
.isFullScreenEntered(value)booleanβ€”Controls whether the Journey is visible in full-screen. Can be called before embedding (sets initial state) or after (updates the live element). See Full-Screen.

Injection methods​

These methods insert the Journey into the DOM and return the Embedding instance. Call exactly one at the end of your chain.

MethodBehavior
.append(selector \| element)Inserts the Journey as the last child of the target element.
.prepend(selector \| element)Inserts the Journey as the first child of the target element.
.after(selector \| element)Inserts the Journey immediately after the target element (as a sibling).
.before(selector \| element)Inserts the Journey immediately before the target element (as a sibling).

Instance methods​

These are called on the Embedding instance returned by an injection method, for dynamic updates after the Journey has already been rendered.

MethodDescription
.mode(value)Updates the display mode on the live element. Used in the Inline to Full-Screen pattern to promote or demote a web component between modes.
.isFullScreenEntered(value)Dynamically enters or exits full-screen on the already-rendered Journey.
.remove()Removes the Journey element from the DOM and cleans up all event listeners.
.el()Returns the raw HTMLElement (or null if not yet injected).

Release Channels​

The SDK auto-loads the web component script from the stable channel by default. To use the canary channel (latest unreleased changes), call .canary() before .asWebComponent():

$epilot
.embed('<your-journey-id>')
.canary()
.asWebComponent()
.mode('inline')
.append('#embed-target')
One channel per page

The browser only allows a custom element to be registered once. This means:

  • Only one release channel (stable or canary) can be active per page.
  • The first .asWebComponent() call determines the channel for all subsequent embeddings on that page.
  • Mixing .canary() and non-canary embeddings on the same page is not supported.

Scenarios​

Inline​

Renders the Journey directly within the page at the position of #embed-target:

Inline
<div id="embed-target"></div>

<script>
document.addEventListener('DOMContentLoaded', function () {
$epilot
.embed('<your-journey-id>')
.asWebComponent()
.mode('inline')
.topBar(true)
.scrollToTop(true)
.append('#embed-target')
})
</script>

Full-Screen Mode​

In full-screen mode the Journey is hidden by default. Call .isFullScreenEntered(true) on the Embedding instance to open it β€” typically from a button click:

Full-screen with a button
<button id="open-btn">Open Journey</button>
<div id="embed-target"></div>

<script>
document.addEventListener('DOMContentLoaded', function () {
var embedding = $epilot
.embed('<your-journey-id>')
.asWebComponent()
.mode('full-screen')
.topBar(true)
.closeButton(true)
.append('#embed-target')

document.getElementById('open-btn').addEventListener('click', function () {
embedding.isFullScreenEntered(true)
})
})
</script>

To close it programmatically (e.g. in response to an event):

embedding.isFullScreenEntered(false)

Inline to Full-Screen Transition​

A common pattern is to start a Journey inline and transition it to full-screen once the user moves past the first step. Listen for EPILOT/USER_EVENT/PAGE_VIEW messages on window and call isFullScreenEntered() accordingly.

Web Component vs iframe

For web components, the full-screen overlay is gated on the element's mode attribute β€” so you must call .mode() alongside isFullScreenEntered() when entering and exiting:

  • Enter: .mode('full-screen').isFullScreenEntered(true)
  • Exit: .isFullScreenEntered(false).mode('inline')

For iframes, the overlay is applied directly via CSS β€” .isFullScreenEntered() alone is sufficient and no mode change is needed.

Inline to full-screen transition (web component)
<div id="embed-target"></div>

<script>
document.addEventListener('DOMContentLoaded', function () {
var journeyId = '<your-journey-id>'
var firstStep = ''

var embedding = $epilot
.embed(journeyId)
.asWebComponent()
.mode('inline')
.topBar(true)
.scrollToTop(true)
.append('#embed-target')

window.addEventListener('message', function (event) {
if (
event.data?.type === 'EPILOT/USER_EVENT/PAGE_VIEW' &&
event.data?.journeyId === journeyId
) {
var path = event.data?.payload?.path

if (!firstStep) {
firstStep = path
} else if (firstStep === path) {
embedding.isFullScreenEntered(false).mode('inline')
} else {
embedding.mode('full-screen').isFullScreenEntered(true)
}
}
})
})
</script>

Launcher Journeys​

Launcher Journeys are a special type of inline Journey where the first step acts as a teaser. When the user navigates past the launcher step, the Journey automatically transitions to full-screen. When they close or navigate back, it returns to inline.

With the SDK, launcher transitions are handled automatically:

  • Web component: The <epilot-journey> custom element handles EPILOT/ENTER_FULLSCREEN and EPILOT/EXIT_FULLSCREEN events internally β€” no extra code needed.
  • iframe: The SDK listens for fullscreen events via postMessage and manages the CSS overlay automatically.
Launcher Journey (web component)
<div id="embed-target"></div>

<script>
document.addEventListener('DOMContentLoaded', function () {
$epilot
.embed('<your-launcher-journey-id>')
.asWebComponent()
.mode('inline')
.topBar(true)
.append('#embed-target')
})
</script>
Launcher Journey (iframe)
<div id="embed-target"></div>

<script>
document.addEventListener('DOMContentLoaded', function () {
$epilot
.embed('<your-launcher-journey-id>')
.asIframe()
.mode('inline')
.topBar(true)
.append('#embed-target')
})
</script>

No event listeners required β€” the SDK and web component handle the transition lifecycle.

Pre-filling Data​

Pre-fill Journey fields and start from a specific step using .dataInjectionOptions(). Combine with .contextData() to pass additional submission metadata:

Data injection
<div id="embed-target"></div>

<script>
document.addEventListener('DOMContentLoaded', function () {
$epilot
.embed('<your-journey-id>')
.asWebComponent()
.mode('inline')
.topBar(true)
.lang('de')
.contextData({ source: 'landing-page' })
.dataInjectionOptions({
initialStepIndex: 1,
blocksDisplaySettings: [
{
type: 'DISABLED',
blockName: 'Address',
stepIndex: 1,
blockFields: ['zipCity'],
},
{
type: 'DISABLED',
blockName: 'Contact Info',
stepIndex: 1,
blockFields: ['lastName'],
},
],
initialState: [
{},
{
Address: { countryCode: 'DE', city: 'KΓΆln', zipCode: '50670' },
'Contact Info': { lastName: 'Mustermann' },
},
],
})
.append('#embed-target')
})
</script>

Context Data​

.contextData() accepts a plain object of key-value pairs. The data is passed to the Journey and included with every submission. Only string and numeric values are supported β€” other types are ignored.

$epilot
.embed('<your-journey-id>')
.asWebComponent()
.mode('inline')
.contextData({ source: 'checkout', campaign: 'summer-2025', count: 3 })
.append('#embed-target')

The Journey also automatically picks up URL search parameters from the host page. Values passed via .contextData() take precedence when keys overlap.


Data Injection​

.dataInjectionOptions() accepts a DataInjectionOptions object:

DataInjectionOptions
type DataInjectionOptions = {
/** The step index to start the Journey from (0-based) */
initialStepIndex?: number
/** Pre-fill data for each step */
initialState?: Record<string, unknown>[]
/** Control which blocks/fields are disabled */
blocksDisplaySettings?: BlockDisplaySetting[]
}

type BlockDisplaySetting = {
type: 'DISABLED'
blockName: string
stepIndex: number
blockFields?: string[]
}

initialState is an array where each element corresponds to a Journey step (by index). Steps that should not be pre-filled must be empty objects {}. The array must be ordered sequentially to match step order.

To discover the correct block names and field structure, open your Journey in debug mode from the Journey Builder and inspect the state per step.


Events​

The SDK dispatches the same events as the Web Component β€” on the window object using postMessage. Listen for them with window.addEventListener:

EventDescription
EPILOT/JOURNEY_LOADEDThe Journey finished loading.
EPILOT/EXIT_FULLSCREENThe Journey exited full-screen mode.
EPILOT/ENTER_FULLSCREENThe Journey entered full-screen mode.
EPILOT/CLOSE_JOURNEYThe user closed the Journey.
EPILOT/FORM_EVENTA form-level event occurred (e.g. submission).
EPILOT/USER_EVENT/PAGE_VIEWThe user navigated to a new step.
EPILOT/USER_EVENT/PROGRESSThe user made progress in the Journey.
Listening for journey events
window.addEventListener('message', function (event) {
if (event.data?.type === 'EPILOT/JOURNEY_LOADED') {
console.log('Journey loaded!')
}

if (event.data?.type === 'EPILOT/USER_EVENT/PAGE_VIEW') {
console.log('Step viewed:', event.data?.payload?.path)
}

if (event.data?.type === 'EPILOT/CLOSE_JOURNEY') {
console.log('Journey closed by user')
}
})
Reacting to close in full-screen

When the user clicks the close button inside a full-screen Journey, the Journey dispatches EPILOT/CLOSE_JOURNEY. The SDK handles this automatically for pure full-screen embeds (mode set to 'full-screen' from the start). For inline-to-fullscreen transitions you need to listen manually and also restore the mode:

// Pure full-screen β€” SDK handles this automatically, no listener needed.

// Inline-to-fullscreen β€” restore mode manually on close:
window.addEventListener('message', function (event) {
if (
event.data?.type === 'EPILOT/CLOSE_JOURNEY' &&
event.data?.journeyId === '<your-journey-id>'
) {
embedding.isFullScreenEntered(false).mode('inline')
}
})

Migrating from the Legacy __epilot API​

If you previously used the __epilot.init() / __epilot.enterFullScreen() API with the bundle.js script, the SDK replaces all of that with a single, consistent interface.

Replace the script:

- <script src="https://embed.journey.epilot.io/bundle.js"></script>
+ <script src="https://embed.journey.epilot.io/sdk/bundle.js"></script>

Replace __epilot.init() with the SDK builder:

- __epilot.init([{ journeyId: '<id>', mode: 'full-screen', topBar: false }])
+ var embedding = $epilot
+ .embed('<id>')
+ .asWebComponent()
+ .mode('full-screen')
+ .topBar(false)
+ .append(document.body)

Replace __epilot.enterFullScreen() / __epilot.exitFullScreen():

- __epilot.enterFullScreen('<id>')
+ embedding.isFullScreenEntered(true)

- __epilot.exitFullScreen('<id>')
+ embedding.isFullScreenEntered(false)

Replace __epilot.on() event listeners:

The SDK no longer uses __epilot.on(). Listen for events directly on window using the same EPILOT/* event type strings. See Events above.


Content-Security-Policy (CSP)​

The SDK uses the same script origin as the Web Component embed. See the Web Components CSP guide for the required policy directives.