S
Stepyo SDKv2.0

Interactive Tutorials SDK

Add interactive step-by-step tutorials to your product. Your users learn by doing, right inside your app.

Quick Start

Quick Start (2 minutes)

Paste the snippet below in the head tag of your site:

html
<script
  src="https://app.stepyo.com.br/sdk.js"
  data-api-key="YOUR_API_KEY"
  data-lang="en"
  async
></script>

Done. The help widget appears automatically in the bottom-right corner.

Tip: use a versioned URL like [email protected] to pin a specific version.

Installation

Installation Methods

Script tag (recommended)

The simplest approach. Works with any framework or static site.

html
<script
  src="https://app.stepyo.com.br/sdk.js"
  data-api-key="YOUR_API_KEY"
  data-lang="en"
  data-position="bottom-right"
  data-color="#EC4899"
  data-button-text="Need help?"
  data-title="Help Center"
  data-icon="help"
  data-user-id="user_123"
  data-user-email="[email protected]"
  async
></script>

React / Next.js

Create a client component that loads the SDK:

tsx
// components/StepyoWidget.tsx
'use client';
import { useEffect } from 'react';

export function StepyoWidget() {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = 'https://app.stepyo.com.br/sdk.js';
    script.async = true;
    script.dataset.apiKey = process.env.NEXT_PUBLIC_STEPYO_KEY!;
    script.dataset.lang = 'en';
    document.head.appendChild(script);

    return () => {
      (window as any).Stepyo?.destroy();
      script.remove();
    };
  }, []);

  return null;
}

// In your layout: <StepyoWidget />

Vue / Nuxt

Use a composable to load the SDK:

vue
<!-- StepyoWidget.vue -->
<script setup>
import { onMounted, onUnmounted } from 'vue';

const props = defineProps({ apiKey: String });

onMounted(() => {
  const s = document.createElement('script');
  s.src = 'https://app.stepyo.com.br/sdk.js';
  s.dataset.apiKey = props.apiKey;
  s.async = true;
  document.head.appendChild(s);
});

onUnmounted(() => window.Stepyo?.destroy());
</script>

npm package

Install the SDK via npm for better TypeScript support and tree-shaking:

bash
npm install @stepyo/sdk

Then use it in your code:

ts
import Stepyo from '@stepyo/sdk';

Stepyo.init({
  apiKey: 'your_api_key_here',
  lang: 'en',
});

You can check the current SDK version at runtime with Stepyo.version.

Server SDKs

Server-side SDKs

Use the server SDKs to interact with the Stepyo API from your backend. Useful for triggering flows, tracking events, and handling webhooks.

Node.js

npm install @stepyo/server

Python

pip install stepyo

Node.js

Install:

bash
npm install @stepyo/server

Usage:

ts
import { StepyoServer } from '@stepyo/server';

const stepyo = new StepyoServer({
  apiKey: process.env.STEPYO_SECRET_KEY,
});

// List all active flows
const flows = await stepyo.listFlows();

// Get a specific flow with steps
const flow = await stepyo.getFlow('welcome-tour');

// AI-powered flow suggestion
const suggestion = await stepyo.ask('How do I create a report?');

// Track a custom event
await stepyo.trackEvent({
  name: 'onboarding_completed',
  userId: 'user_123',
  properties: { plan: 'pro' },
});

Python

Install:

bash
pip install stepyo

Usage:

python
from stepyo import StepyoServer

stepyo = StepyoServer(api_key="your_secret_key")

# List all active flows
flows = stepyo.list_flows()

# Get a specific flow with steps
flow = stepyo.get_flow("welcome-tour")

# AI-powered flow suggestion
suggestion = stepyo.ask("How do I create a report?")

# Track a custom event
stepyo.track_event(
    name="onboarding_completed",
    user_id="user_123",
    properties={"plan": "pro"},
)

Framework integration

Handle webhooks in your framework of choice:

python
# Flask
from flask import Flask, request, jsonify
from stepyo import StepyoServer

app = Flask(__name__)
stepyo = StepyoServer(api_key="your_secret_key")

@app.route("/webhooks/stepyo", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Stepyo-Signature", "")
    if not stepyo.verify_webhook(request.data, signature):
        return jsonify({"error": "Invalid signature"}), 401
    event = request.json
    print(f"Received: {event['event']}")
    return jsonify({"ok": True})

# FastAPI
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/webhooks/stepyo")
async def handle_webhook(request: Request):
    body = await request.body()
    signature = request.headers.get("X-Stepyo-Signature", "")
    if not stepyo.verify_webhook(body, signature):
        raise HTTPException(status_code=401)
    event = await request.json()
    return {"ok": True}

# Django
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def stepyo_webhook(request):
    signature = request.headers.get("X-Stepyo-Signature", "")
    if not stepyo.verify_webhook(request.body, signature):
        return JsonResponse({"error": "Invalid signature"}, status=401)
    import json
    event = json.loads(request.body)
    return JsonResponse({"ok": True})
JS API

JavaScript API Reference

All methods are available on window.Stepyo:

Stepyo.init(config)

Initializes the SDK manually (alternative to auto-init via script tag).

js
Stepyo.init({
  apiKey: 'your_api_key_here',
  lang: 'en',               // 'en' | 'pt-BR'
  position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
  primaryColor: '#EC4899',  // hex
  buttonText: 'Help',       // button label (empty = icon only)
  panelTitle: 'Tutorials',
  buttonIcon: 'help',       // 'help'|'chat'|'book'|'zap'|'sparkles'|'headphones' or URL
});

Stepyo.identify(user)

Associates SDK events with the user in your app. Call after login.

js
Stepyo.identify({
  userId: 'user_123',
  email: '[email protected]',
  name: 'Jane Smith',
  traits: { role: 'admin', plan: 'pro' }
});

Stepyo.startFlow(flowKey)

Starts a specific tutorial by key. Ideal for contextual onboarding.

js
// Trigger tutorial when user creates an account
Stepyo.startFlow('welcome-tour');

Stepyo.openPanel() / Stepyo.closePanel()

Opens or closes the tutorials panel programmatically.

Stepyo.isReady()

Returns true when the SDK has finished loading.

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

Listen to SDK events.

js
Stepyo.on('flow:completed', (data) => {
  console.log('Tutorial completed:', data.flowKey);
  // Mark onboarding as complete in your backend
});

Stepyo.on('ready', () => {
  // SDK loaded, safe to use startFlow
});

Stepyo.destroy()

Removes the widget and cleans up listeners. Use in SPAs when unmounting the component.

Stepyo.version

Returns the current SDK version string (e.g. "2.0.0").

js
console.log(Stepyo.version); // "2.0.0"

Configuration options

AttributeTypeDescription
data-api-keystringAPI key (required)
data-langen | pt-BRLanguage (default: en)
data-positionstringButton position
data-colorhexWidget primary color (hex)
data-button-textstringButton label (empty = icon only)
data-titlestringPanel title
data-iconstringPreset icon or image URL
data-user-idstringUser ID
data-user-emailstringUser email
data-user-namestringUser name

Events

EventDataWhen fired
ready-SDK initialized
flow:started{flowKey, flowName}Tutorial started
flow:completed{flowKey}Tutorial completed
flow:cancelled{flowKey, lastStep}User cancelled
step:changed{flowKey, stepIndex, totalSteps}Advanced/went back a step
step:failed{flowKey, stepIndex, error}Element not found
panel:opened-Tutorials panel opened
panel:closed-Panel closed

Deep linking

Open a specific tutorial via URL:

url
https://yourapp.com/dashboard?stepyo-flow=welcome-tour
Webhooks

Webhooks

Webhooks let your server receive real-time notifications when events happen in the SDK. Configure webhooks in the dashboard or via the REST API.

How to configure

Go to Settings > SDK > Webhooks in your dashboard, or use the REST API endpoints below.

Available events

Subscribe to specific events or use * to receive all:

EventDescription
flow_startedA tutorial was started
flow_completedA tutorial was completed
flow_cancelledA tutorial was cancelled
step_failedA step element was not found
chat_escalatedUser escalated from AI chat
widget_openedThe widget was opened
*All events

Payload example

json
{
  "id": "evt_abc123",
  "event": "flow_completed",
  "timestamp": "2026-03-19T12:00:00Z",
  "data": {
    "flow_key": "welcome-tour",
    "flow_name": "Welcome Tour",
    "user_id": "user_123",
    "user_email": "[email protected]",
    "duration_ms": 45200
  },
  "workspace_id": "ws_xyz"
}

Signature verification

Every webhook request includes a X-Stepyo-Signature header containing an HMAC SHA-256 signature. Verify it to ensure the request came from Stepyo.

Node.js verification

js
import crypto from 'crypto';

function verifyWebhookSignature(body, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// Express middleware
app.post('/webhooks/stepyo', express.raw({ type: '*/*' }), (req, res) => {
  const sig = req.headers['x-stepyo-signature'];
  if (!verifyWebhookSignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  const event = JSON.parse(req.body);
  console.log('Event:', event.event);
  res.json({ ok: true });
});

Python verification

python
import hmac
import hashlib

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Auto-disable behavior

If your endpoint returns a non-2xx status code for 10 consecutive deliveries, the webhook is automatically disabled. You can re-enable it in the dashboard.

Retry policy

Failed deliveries are retried up to 3 times with exponential backoff (1 min, 5 min, 30 min).

REST API

REST API Reference

All endpoints are relative to https://app.stepyo.com.br. API key endpoints use the x-stepyo-key header. Session endpoints require an authenticated browser session.

EndpointMethodAuthDescription
/api/sdk/initPOSTAPI KeyInitialize SDK session
/api/sdk/flowsGETAPI KeyList active flows
/api/sdk/flows/:keyGETAPI KeyGet flow with steps
/api/sdk/askPOSTAPI KeyAI flow suggestion
/api/sdk/eventsPOSTAPI KeyTrack event
/api/sdk/chatPOSTAPI KeyChat (SSE stream)
/api/sdk/webhooksGETSessionList webhooks
/api/sdk/webhooksPOSTSessionCreate webhook
/api/sdk/webhooks/:idPATCHSessionUpdate webhook
/api/sdk/webhooks/:idDELETESessionDelete webhook
/api/sdk/webhooks/:id/testPOSTSessionTest webhook delivery
/api/sdk/webhooks/:id/logsGETSessionView delivery logs

Authentication

For API-key-authenticated endpoints, include your key in the header:

http
x-stepyo-key: sk_live_abc123...

Rate limits

Default: 60 requests/minute per API key. Configurable per key in the dashboard. When exceeded, the API returns 429 Too Many Requests.

Response format

All responses are JSON. Successful responses include the data directly. Errors follow this format:

json
{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMITED",
  "retryAfter": 30
}
Versioning

Versioning

Latest (always up to date)

sdk.js always points to the latest stable version.

html
<script src="https://app.stepyo.com.br/sdk.js" ...></script>

Pinned version

Pin a specific version to avoid unexpected changes:

html
<script src="https://app.stepyo.com.br/[email protected]" ...></script>

Runtime version check

Check the version at runtime:

js
if (Stepyo.version !== '2.0.0') {
  console.warn('Unexpected SDK version:', Stepyo.version);
}

Breaking change policy

We follow semantic versioning. Breaking changes only happen in major versions and are announced at least 30 days in advance via email and the dashboard.

Security

Security

API Keys

  • Keys are hashed with SHA-256 on the server (never stored as plain text)
  • The full key is shown only once (at creation)
  • Keys can be disabled or deleted at any time

Allowed domains

  • Configure which domains can use your API key
  • Supports wildcards: *.yourdomain.com
  • Requests from unauthorized domains are blocked (CORS)

Rate limiting

  • Default: 60 requests/minute per key
  • Configurable per key in the dashboard

Webhook signatures

Every webhook delivery is signed with HMAC SHA-256 using your webhook secret. Always verify signatures before processing payloads.

FAQ

FAQ

The bundle is ~12KB gzip. It loads asynchronously (async) and does not block rendering. Uses Shadow DOM for full CSS isolation.

Yes. The SDK detects client-side navigation and persists tutorial state across pages via sessionStorage.

Use the Stepyo Chrome extension to record tutorials in your product. Then convert them to a flow and activate for the SDK in the dashboard.

Yes. Use Stepyo.startFlow('key') to trigger tutorials programmatically. To hide the floating button, do not set data-button-text and use CSS to hide it if needed.

The SDK only sends the data you pass via identify(). No personal data is collected automatically. Events are stored for 90 days and accessible only through your dashboard.

Configure a webhook in Settings > SDK > Webhooks. Your server will receive POST requests with event payloads whenever SDK events occur. See the Webhooks section for details.

Yes. We offer official SDKs for Node.js (npm install @stepyo/server) and Python (pip install stepyo). See the Server-side SDKs section for usage examples.

Each webhook request includes an X-Stepyo-Signature header. Compute an HMAC SHA-256 of the request body using your webhook secret and compare. See the Webhooks section for code examples.

Stepyo retries failed deliveries up to 3 times with exponential backoff. After 10 consecutive failures, the webhook is automatically disabled. You can re-enable it from the dashboard.