SDK

Installing the SDK

Add the Geyed SDK to your site via npm or CDN, initialize it, and start tours.

Installation

Install the Geyed SDK from npm:

npm install @geyed/sdk

Or load it via CDN if you prefer a script tag. The CDN build is an IIFE bundle that exposes the global Geyed:

<script src="https://unpkg.com/@geyed/sdk/dist/index.iife.js"></script>

Quick start

ESM (npm)

Import init and start in your app's entry point, call init with your API key, then call start to fetch and run the first matching tour:

import { init, start, on } from "@geyed/sdk";

init({
  apiKey: "gyd_your_app_key",
  baseUrl: "https://api.geyed.com",
  debug: true,
});

on("tour_started", (e) => console.log("Tour started:", e.tourName));

await start();

Replace gyd_your_app_key with the API key from your Geyed dashboard. init must be called before start.

IIFE / CDN

<script src="https://unpkg.com/@geyed/sdk/dist/index.iife.js"></script>
<script>
  Geyed.init({
    apiKey: "gyd_your_app_key",
    baseUrl: "https://api.geyed.com",
    debug: true,
  });

  Geyed.start();
</script>

Make sure the IIFE bundle <script> tag appears before your inline script. Use the .iife.js file for CDN usage, not .mjs.

API reference

init(config: GeyedConfig): void

Sets SDK configuration. Must be called before start().

FieldTypeDescription
apiKeystringYour Geyed app key (required for backend fetch).
baseUrlstringBase URL of the Geyed backend (e.g. https://api.geyed.com).
environmentstring?Optional environment tag forwarded with fetch.
userIdstring?Optional end-user identifier for analytics and targeting.
debugboolean?Logs SDK activity to the console when true.
toursSdkTour[]?Preload tours inline instead of fetching from the backend.
import { init } from "@geyed/sdk";

init({
  apiKey: "gyd_your_app_key",
  baseUrl: "https://api.geyed.com",
  userId: "user_123",
  debug: true,
});

start(): Promise<void>

Fetches published tours (or uses preloaded or registered tours) and starts the first tour whose urlPattern matches the current window.location.pathname.

await start();

startTour(tourId: number): Promise<void>

Starts a specific tour by ID, bypassing URL matching. Useful for triggering a tour programmatically from a button or onboarding flow.

await startTour(42);

stop(): void

Stops the active tour and removes any overlays and tooltips from the page.

stop();

destroy(): void

Full cleanup: stops any active tour and removes injected styles. Call this when unmounting your application or navigating away in a single-page app.

destroy();

registerTours(tours: SdkTour[]): void

Registers tours locally without making a backend fetch. Useful for offline demos or self-hosted tour definitions.

import { registerTours, start } from "@geyed/sdk";

registerTours([
  {
    tourId: 1,
    tourName: "Welcome Tour",
    versionId: 1,
    urlPattern: "/dashboard",
    triggerType: "auto",
    triggerSelector: null,
    isRepeatable: false,
    themeConfig: null,
    transitionPreset: null,
    steps: [],
  },
]);

await start();

on(event, callback) / off(event, callback)

Subscribe or unsubscribe from SDK lifecycle events. See the Events section below for available event names and payload shapes.

import { on, off } from "@geyed/sdk";

const handler = ({ tourName, stepIndex, stepTitle }) => {
  myAnalytics.track("geyed_step_viewed", { tourName, stepIndex, stepTitle });
};

on("step_viewed", handler);

// Later, to unsubscribe:
off("step_viewed", handler);

Defining tours in code

Tours do not have to live in the Geyed dashboard. You can define them directly in your codebase and hand them to the SDK at runtime — no backend fetch required. This is useful for offline demos, self-hosted deployments, and situations where you want tour content to be version-controlled alongside your application code rather than stored in an external service.

There are two ways to supply locally-defined tours: pass them to init() via the tours field, or call registerTours() at any point before start(). Both approaches are equivalent in result — the SDK uses your local definitions instead of fetching from the backend for those tours.

Inline via init

If your tour set is known at boot time, pass it directly to init() via the tours field. The SDK registers every tour in the array immediately during initialization, so by the time you call start() no network request is needed.

import { init, start } from "@geyed/sdk";

init({
  apiKey: "gyd_your_app_key",
  baseUrl: "https://api.geyed.com",
  tours: [
    {
      tourId: 1,
      tourName: "Dashboard Quickstart",
      versionId: 1,
      urlPattern: "/dashboard",
      triggerType: "auto",
      triggerSelector: null,
      isRepeatable: false,
      themeConfig: null,
      transitionPreset: null,
      steps: [
        {
          id: 1,
          order: 1,
          title: "Welcome to your dashboard",
          content: "This is where you can see an overview of all your activity at a glance.",
          targetSelector: "#dashboard-overview",
          placement: "bottom",
          transitionPreset: null,
        },
      ],
    },
  ],
});

await start();

After init via registerTours

If you need to supply tours after init() has already been called — for example, once you know which plan the user is on, or after a lazy-loaded module resolves — call registerTours() with your array of tours. You can call it before or after init(), but it must be called before start().

import { init, registerTours, start } from "@geyed/sdk";

init({
  apiKey: "gyd_your_app_key",
  baseUrl: "https://api.geyed.com",
});

// Called later, e.g. after the user's role is resolved:
registerTours([
  {
    tourId: 1,
    tourName: "Dashboard Quickstart",
    versionId: 1,
    urlPattern: "/dashboard",
    triggerType: "auto",
    triggerSelector: null,
    isRepeatable: false,
    themeConfig: null,
    transitionPreset: null,
    steps: [
      {
        id: 1,
        order: 1,
        title: "Welcome to your dashboard",
        content: "This is where you can see an overview of all your activity at a glance.",
        targetSelector: "#dashboard-overview",
        placement: "bottom",
        transitionPreset: null,
      },
    ],
  },
]);

await start();

Each call to registerTours() merges the provided tours into the local set, keyed by tourId. Calling it a second time with a tour that shares an existing tourId overwrites that entry; tours with different IDs are unaffected.

Field reference

SdkTour fields

FieldTypeRequiredNotes
tourIdnumberyesAny positive integer you pick. Used by analytics events and as a handle for startTour().
tourNamestringyesDisplay name. Shown in analytics events.
versionIdnumberyesVersion identifier. For code-defined tours, pick any integer (use 1 if you're not versioning locally).
urlPatternstring | nullyesGlob pattern matched against window.location.pathname. * is a wildcard. Examples: '/dashboard/*', '/settings', '/users/*/profile'. Use null, '*', or '/*' to match every URL.
triggerType'auto' | 'click' | 'external'yes'auto' starts the tour on the first matching URL visit. 'click' waits for a click on triggerSelector. 'external' means only a manual startTour() call will start it.
triggerSelectorstring | nullyesCSS selector listened on when triggerType is 'click'. Set to null for 'auto' and 'external'.
isRepeatablebooleanyesIf true, the tour can run multiple times for the same user. If false, it runs once per user.
themeConfigstring | nullyesJSON-stringified theme config. See below. null uses the default light theme.
transitionPreset'smooth' | 'snappy' | 'fade' | 'slide' | 'none' | nullyesTour-level transition animation. null falls back to the default ('smooth'). Individual steps can override this.
stepsSdkStep[]yesOrdered array of steps.

SdkStep fields

FieldTypeRequiredNotes
idnumberyesAny positive integer unique within the tour.
ordernumberyesZero-based or one-based ordering index. Steps render in ascending order.
titlestringyesTooltip heading.
contentstringyesTooltip body text. Supports plain text; rich formatting is applied when tours are authored in the dashboard.
targetSelectorstringyesCSS selector pointing at the DOM element the tooltip should attach to. The SDK retries selector resolution briefly before skipping a missing step.
placement'top' | 'bottom' | 'left' | 'right'yesWhich side of the target element the tooltip renders on.
transitionPreset'smooth' | 'snappy' | 'fade' | 'slide' | 'none' | nullyesStep-level transition override. null inherits the tour-level preset.

themeConfig

themeConfig is a JSON-serialized object. The parsed shape is:

{
  preset: "light" | "dark" | "blue" | "custom";
  primaryColor?: string;     // only used when preset is "custom"
  backgroundColor?: string;  // only used when preset is "custom"
  textColor?: string;        // only used when preset is "custom"
  transitionPreset?: "smooth" | "snappy" | "fade" | "slide" | "none";
}

For a built-in preset, stringify a minimal object:

themeConfig: JSON.stringify({ preset: "dark" }),

For custom colors, set preset: "custom" and supply the color fields:

themeConfig: JSON.stringify({
  preset: "custom",
  primaryColor: "#2563eb",
  backgroundColor: "#0f172a",
  textColor: "#f8fafc",
}),

Colors accept any CSS color string (hex, rgb, hsl, named). When a custom field is omitted, the SDK falls back to sensible defaults. If the JSON fails to parse, the SDK falls back to the light preset.

When to use which

Use init({ tours }) for a static set of tours that is always present and known at the time your app boots. Use registerTours() when you need to lazy-load tour definitions — for example after a user logs in and you know their role — or when you want to conditionally register different tours based on runtime application state.

Note: When local tours are registered, the SDK does not make a backend fetch for those tours. You still need to call start() to begin URL matching and run the first tour whose urlPattern matches the current page.

Events

Subscribe to lifecycle events with on and unsubscribe with off.

tour_started

Fires when a tour begins. Payload: { tourId, tourName }.

FieldTypeDescription
tourIdnumberNumeric ID of the tour.
tourNamestringDisplay name of the tour.

tour_completed

Fires when a user reaches the final step and advances past it. Payload: { tourId, tourName }.

FieldTypeDescription
tourIdnumberNumeric ID of the tour.
tourNamestringDisplay name of the tour.

tour_dismissed

Fires when a user closes a tour before completing it. Payload: { tourId, tourName, stepIndex }.

FieldTypeDescription
tourIdnumberNumeric ID of the tour.
tourNamestringDisplay name of the tour.
stepIndexnumberZero-based index of the step the user was on when they dismissed.

step_viewed

Fires each time a step becomes visible. Payload: { tourId, tourName, stepIndex, stepTitle }.

FieldTypeDescription
tourIdnumberNumeric ID of the tour.
tourNamestringDisplay name of the tour.
stepIndexnumberZero-based index of the step.
stepTitlestringTitle of the step.
import { on } from "@geyed/sdk";

on("step_viewed", ({ tourName, stepIndex, stepTitle }) => {
  myAnalytics.track("geyed_step_viewed", { tourName, stepIndex, stepTitle });
});

Browser support

BrowserMinimum version
Chrome / Edge90
Firefox88
Safari15.4

The SDK requires fetch, Promise, URLSearchParams, and modern DOM APIs. Internet Explorer is not supported.

Runtime behaviour

When start() is called, the SDK fetches published tours from GET {baseUrl}/api/v1/sdk/tours using your API key. It compares each tour's urlPattern against the current window.location.pathname and runs the first match. For each step, it resolves the targetSelector with retries before skipping a step whose element cannot be found. Once a step is visible it renders an overlay and tooltip with Next, Back, and Close controls and emits the appropriate lifecycle events through the on/off event bus.

Build formats

The npm package ships three output formats in dist/:

FileFormatUse case
index.mjsESMBundlers (Vite, webpack, esbuild)
index.cjsCommonJSNode.js or older bundlers
index.iife.jsIIFE (minified)Direct <script> tag, exposes window.Geyed

TypeScript declarations are included as index.d.ts. Source maps are provided for all formats.