JavaScript API reference

Once the StepsKit script has loaded, it exposes a global window.stepskit object with the methods documented below.

Pre-init queue

Every method below can be called before the SDK script finishes loading. The install snippet creates window.stepskit immediately and installs a shim for each queueable method that pushes the call onto window.stepskit._q. When the embed initializes, it replays the queue in order against the real instance — so identify, setUserAttributes, and friends "just work" without an <Script onLoad> callback or window.stepskit?.method(...) null check.

The only exceptions are the synchronous getters — isPlaying, getTours, and getUserAttributes. These return values immediately, so they require the SDK to be loaded. Calling them pre-init throws a TypeError because the queue stub doesn't define them.

User attributes

identify

identify(visitor: Record<string, string | number | boolean>): Promise<void>

Alias for setUserAttributes(visitor, { autoRefresh: true }). The shape matches Pendo and Intercom-style SDKs, so existing identify patterns translate directly. Safe to call before the SDK script finishes loading — the call is queued and replayed at init, in time for the first tour fetch.

stepskit.identify({ id: "u_123", email: "a@b.com", plan: "pro" });

No ?. null check needed — the install snippet's queue stub captures the call even if the real SDK hasn't downloaded yet.

setUserAttributes

setUserAttributes(
  attrs: Record<string, string | number | boolean>,
  options?: { autoRefresh?: boolean },
): Promise<void>

Merges attrs into the visitor's known attributes. Passing autoRefresh: true re-fetches tours and re-evaluates visibility against the new attributes — call this after login or any change that affects targeting. identify is the preferred shape for the post-login case; setUserAttributes is for partial updates without a refresh.

await window.stepskit?.setUserAttributes(
  { id: "user_123", plan: "enterprise" },
  { autoRefresh: true },
);

getUserAttributes

getUserAttributes(): Record<string, string | number | boolean> | undefined

Returns the current attributes, or undefined if none have been set. This is a synchronous getter — it requires the SDK to be loaded, so calling it before the script finishes loading throws.

Tour control

playTour

playTour(tourId: string): Promise<void>

Starts the named tour immediately, regardless of targeting rules. Useful for "Take a tour" buttons.

You don't need any JavaScript to trigger a tour from a link. Add a sk query parameter with the tour's id or slug to any page URL on your site:

https://app.example.com/dashboard?sk=welcome-tour

When the embed loads and sees ?sk=, it force-plays that tour immediately — bypassing URL, targeting, screen-width, and frequency rules (it plays even if the visitor already saw a show_once tour). The tour must still be published to that domain. Great for "check out this new feature" links in changelogs, emails, or support replies. Point the link at the page where the tour's first step appears. Copy a ready-made link from the tour's Publish page.

stopTour

stopTour(): void

Stops the currently playing tour. Counts as a dismissal for frequency capping.

isPlaying

isPlaying(): boolean

Returns true if a tour is currently on screen.

getTours

getTours(): Array<{ id: string; name: string }>

Returns the list of tours currently loaded for this visitor (i.e., the tours that passed targeting and frequency rules at the last fetch).

Re-evaluation

refresh

refresh(): Promise<void>

Re-fetches tours from the StepsKit API with the current user attributes and re-evaluates which one (if any) should play now. Call this after client-side route changes if your app navigates without full page reloads and you want tours to react to the new page.

setUserAttributes(..., { autoRefresh: true }) calls refresh for you; you only need refresh() directly when attributes haven't changed but context has.

Events

on / off

on(event: string, handler: (...args: unknown[]) => void): void
off(event: string, handler: (...args: unknown[]) => void): void

Subscribe and unsubscribe to embed events. Available events include:

  • announcement_clicked — fired when a banner CTA is clicked.
window.stepskit?.on("announcement_clicked", (payload) => {
  console.log("announcement clicked", payload);
});

Announcements

dismissAnnouncement

dismissAnnouncement(announcementId: string): void

Programmatically dismiss a banner — equivalent to the user clicking the close button.

Teardown

destroy

destroy(): void

Tears down all StepsKit UI (active tours and announcements) and detaches listeners. Useful in single-page apps when the user signs out and you want to fully reset the embed.

Diagnostics

validateEnvironment

validateEnvironment(): EnvironmentReport

Returns a diagnostic snapshot of the embed's current state — handy when a tour you expected to fire isn't firing. The method also logs the filtered-tour list via console.table and the full report via console.log, so the simplest workflow is to open devtools and run:

const report = stepskit.validateEnvironment();

The returned EnvironmentReport includes:

  • apiKey — the masked project API key in use.
  • baseUrl — the API endpoint the embed is targeting.
  • visitorId — the resolved visitor identifier, or undefined if no id was provided.
  • userAttributes — the current attribute bag (or undefined).
  • initialized — whether init() has completed.
  • isPlaying — whether a tour is currently on screen.
  • currentTourId — the ID of the currently-playing tour, or null.
  • toursLoaded / hintsLoaded — counts of tours and hints fetched.
  • toursFiltered — array of { tourId, name, reason, detail? } for every tour that was loaded but filtered out before playback.
  • warnings — validation messages accumulated since init (e.g. dropped attribute keys from setUserAttributes).

The reason field on toursFiltered is one of:

  • url_pattern_mismatch — the current URL doesn't match the tour's URL rules.
  • screen_width_too_narrow — the viewport is below the tour's minimum width.
  • targeting_failed — the visibility rules didn't match the current visitor's attributes.
  • frequency_cappedshow_once already fired for this visitor.
  • inactive — the tour is currently disabled in the dashboard.

This is a pure dev/debug call — safe to invoke any time post-init, but don't ship it in production code paths.

TypeScript

The @stepskit/embed package ships types at dist/types/index.d.ts. For most projects you'll only need an ambient declaration for the global:

type StepsKitAttributes = Record<string, string | number | boolean>;

interface ToursFilteredEntry {
  tourId: string;
  name: string;
  reason:
    | "url_pattern_mismatch"
    | "screen_width_too_narrow"
    | "targeting_failed"
    | "frequency_capped"
    | "inactive";
  detail?: string;
}

interface EnvironmentReport {
  apiKey: string;            // masked (e.g. "sk_live_a3f...***")
  baseUrl: string;
  visitorId: string | undefined;
  userAttributes: StepsKitAttributes | undefined;
  initialized: boolean;
  isPlaying: boolean;
  currentTourId: string | null;
  toursLoaded: number;
  hintsLoaded: number;
  toursFiltered: ToursFilteredEntry[];
  warnings: string[];
}

interface Window {
  stepskit?: {
    identify(visitor: StepsKitAttributes): Promise<void>;
    setUserAttributes(
      attrs: StepsKitAttributes,
      options?: { autoRefresh?: boolean },
    ): Promise<void>;
    getUserAttributes(): StepsKitAttributes | undefined;
    playTour(tourId: string): Promise<void>;
    stopTour(): void;
    isPlaying(): boolean;
    getTours(): Array<{ id: string; name: string }>;
    refresh(): Promise<void>;
    on(event: string, handler: (...args: unknown[]) => void): void;
    off(event: string, handler: (...args: unknown[]) => void): void;
    dismissAnnouncement(id: string): void;
    destroy(): void;
    validateEnvironment(): EnvironmentReport;
  };
}