DocsQuick Start Guide
Developer Docs

Quick Start Guide

Seamlessly integrate interactive Stories into your web and mobile applications in minutes. Choose your platform and launch engaging content with full analytics, targeting, and personalization.

Authentication & Token

Every SDK (Web, React, iOS) authenticates requests to Storyfyd using a dedicated application token that identifies your project and account.

Getting your application token

  • Log in to the Storyfyd dashboard (storyfyd.io).
  • Navigate to: Settings → SDK.
  • Copy your application token and pass it to your init() or configure() method.

Security best practices

Never hardcode your application token directly in client-side bundles. Fetch it securely from your own backend at runtime.

server.js
// ❌ Źle — token widoczny w źródle strony
storyfydWeb.init({ token: 'eyJ0eXAiOiJKV1...' });
// ✅ Dobrze — token pobrany z własnego backendu w runtime
const res = await fetch('/api/storyfyd-token');
const { token } = await res.json();
storyfydWeb.init({ token });

User & Targeting (Audiences)

The user object passed on SDK initialization identifies the viewer and powers two key features: content targeting via tags (labels) and dynamic in-story personalization (custom).

Passing user data from the SDK

The id field is sent via the X-User-Id header. The remaining attributes (name, email, labels, custom) are JSON-serialized into a single X-User header. The exact same format applies across Web, React, and iOS SDKs.

user.js
user: {
id: 'user123', // → nagłówek X-User-Id
name: 'John Doe',
labels: ['vip', 'lang_pl'], // steruje targetowaniem Audiences
custom: { plan: 'pro' } // dostępne tylko w tokenach personalizacji {{plan}}
}
// name, email, labels, custom trafiają razem do wspólnego nagłówka X-User (JSON)

Targeting with labels in the dashboard

  • In the Storyfyd dashboard, go to Audiences and create a new Audience of type "Labels".
  • Name it exactly as the label value sent in the SDK's labels array (e.g. label "vip" requires an Audience named "vip") — the match is exact.
  • In Story Group settings, add an include or exclude rule referencing that Audience to show or hide content for viewers matching that label.

Example use case: language-targeted content (PL / EN)

Labels provide an effortless way to display different Story Groups based on user language or customer segment — without touching SDK code, configured entirely in the dashboard.

  • On SDK init, pass labels based on the user's selected language, e.g. labels: ['lang_pl'] or labels: ['lang_en'].
  • In Audiences, create two Labels-type Audiences: "lang_pl" and "lang_en".
  • Create dedicated Story Groups for each language and assign the matching Audience as an include rule — Polish customers see Polish Stories, and English-speaking users see English Stories.

Custom properties & content personalization

Fields passed under custom (e.g. { plan: 'pro' }) do not affect Audience targeting — they are stored on the viewer's profile and used exclusively for resolving personalization tokens ({{plan}}) inside the story creator.

Where user data comes from

Storyfyd does not manage your end-user sessions — your own application knows who is currently logged in. Fetch their id, email, labels, and custom attributes from your auth/database layer and pass them to the SDK on initialization.

Web Component (Vanilla JS / HTML)

Embed interactive Stories into any website in 3 simple steps. Include the Storyfyd script, add the <storyfyd-web> component, and initialize with your token.

index.html
<!-- Step 1: Load the SDK -->
<script src="https://cdn.storyfyd.io/sdk/latest/storyfyd-web.js"></script>
<!-- Step 2: Add the Widget -->
<storyfyd-web></storyfyd-web>
<!-- Step 3: Initialize -->
<script>
const storyfydWeb = document.querySelector('storyfyd-web');
storyfydWeb.init({
token: 'YOUR_TOKEN_HERE',
user: {
id: 'user123',
name: 'John Doe',
},
layout: {
view: 'classic',
style: { width: '100px', height: '100px' }
},
onReady: () => console.log('Stories loaded!'),
onStoryOpen: (data) => console.log('Story opened:', data),
onNoStories: () => console.log('No stories available')
});
</script>

Full list of init() parameters

PropTypeRequiredDescription
tokenstring✅Your Storyfyd API token.
userobject❌User object with id (required if provided) and optional custom attributes.
testModeboolean❌Enables test mode, sending the X-Test-Mode: true header (default: false).
layoutobject❌Widget layout and visual presentation configuration.
containerstring | Element❌CSS selector or DOM element where the widget should render.
onReadyfunction❌Triggered when the widget is fully loaded and ready.
onStoryOpenfunction❌Triggered when a story opens (receives story payload).
onStoryClosedfunction❌Triggered when the fullscreen story viewer closes.
onStoryChangedfunction❌Triggered when the currently displayed story slide or group changes.
onInteractionfunction❌Triggered on element interaction (CTA clicks, poll answers, emoji reactions).
onErrorfunction❌Triggered when an error occurs.
onNoStoriesfunction❌Triggered when no active stories are available for this user.

React SDK

First-class React component with full TypeScript definitions, custom hooks, and seamless integration into your React tree.

Terminal
$npm install @storyfyd/sdk-react
App.jsx
import { StoryfydWidget } from '@storyfyd/sdk-react';
function App() {
return (
<StoryfydWidget
token="YOUR_TOKEN_HERE"
user={{
id: 'user123',
name: 'John Doe',
labels: ['LANG_PL', 'VIP']
}}
layout={{
view: 'classic',
style: { width: '80px', height: '80px' }
}}
onReady={() => console.log('Stories loaded!')}
onStoryOpen={(data) => console.log('Story opened:', data)}
onStoryClosed={() => console.log('Story closed')}
onNoStories={() => console.log('No stories available')}
/>
);
}

Component props for <StoryfydWidget />

PropTypeRequiredDescription
tokenstring✅Your Storyfyd API token.
userobject❌User data object (requires id field).
layoutobject❌Widget appearance and layout configuration.
classNamestring❌Additional CSS class names for styling.
styleobject❌Inline CSS styles applied to the widget container.
onReadyfunction❌Callback triggered when the widget is ready.
onStoryOpenfunction❌Callback triggered when a story opens.
onStoryClosedfunction❌Callback triggered when the story viewer closes.
onStoryChangedfunction❌Callback triggered when the story slide changes.
onInteractionfunction❌Callback triggered on user interaction (reactions, polls, quizzes, CTA).
onErrorfunction❌Callback triggered on error.

useStoryfyd() hook — programmatic control

PropertyTypeDescription
refRefObjectReact ref to pass to the <storyfyd-web> element.
playfunctionResumes playing the current story.
pausefunctionPauses playback of the current story.
closefunctionProgrammatically closes the story viewer.
navigatefunctionNavigates directly to a specific story: navigate(groupIndex, storyIndex).
destroyfunctionDestroys and cleans up the widget instance.
isInitializedbooleanIndicates whether the widget has already been initialized.
MyComponent.jsx
import { useStoryfyd } from '@storyfyd/sdk-react';
function MyComponent() {
const storyfyd = useStoryfyd('YOUR_TOKEN', {
onReady: () => console.log('Ready!')
});
return (
<div>
<storyfyd-web ref={storyfyd.ref} />
<button onClick={storyfyd.play}>Play</button>
<button onClick={storyfyd.pause}>Pause</button>
<button onClick={storyfyd.close}>Close</button>
<button onClick={() => storyfyd.navigate(0, 0)}>Navigate</button>
</div>
);
}

iOS (Swift) SDK

Native SwiftUI library for iOS 17+, with full support for UIKit applications. Zero external dependencies — built strictly with Apple native APIs.

MyApp.swift
import StoryfydSDK
@main
struct MyApp: App {
init() {
StoryfydSDK.configure(token: "YOUR_APP_TOKEN")
StoryfydSDK.setUser(StoryfydUser(
id: "user_123",
name: "John Doe",
labels: ["vip", "beta"],
custom: ["plan": "pro"]
))
}
var body: some Scene {
WindowGroup {
StoryfydStoriesView(
configuration: StoryfydConfiguration(
layout: .init(view: .classic, width: 80, height: 80),
showTitles: true,
unseenBorderColors: [.purple, .red, .orange],
storyDuration: 7
)
)
}
}
}

configure() parameters

ParameterTypeDefaultDescription
tokenString—Application token generated in the Storyfyd dashboard.
testModeBoolfalseSends the X-Test-Mode: true header with requests for staging/testing.

StoryfydConfiguration — visual styling

ParameterTypeDescription
layout.view.classic | .modern | .minimalVisual layout style for story preview circles.
layout.width / heightCGFloatDiameter of the story preview circle in points.
layout.titleColorColorTitle text color below the circle.
layout.titleLineLimitIntMaximum number of lines for group titles.
showTitlesBoolWhether to display group titles below preview circles.
unseenBorderColors[Color]Gradient border colors for unread stories.
seenBorderColorColorBorder color for already-viewed stories.
storyDurationTimeIntervalDefault display duration for photo stories (seconds).
autoAdvanceBoolWhether to automatically advance to the next slide.
pauseGesture.hold | .tapGesture that pauses story playback (hold or tap).

StoryfydDelegate methods

MethodDescription
storyfydDidOpenStoryGroup(_:)Invoked when a story group opens.
storyfydDidCloseViewer()Invoked when the full-screen stories viewer closes.
storyfydDidChangeStory(groupId:storyId:)Invoked when the current story slide changes.
storyfydDidInteract(elementId:interaction:)Invoked when the user interacts with an interactive widget.
storyfydShouldHandleURL(_:)Determines whether your app (true) or the SDK (false) handles a tapped CTA link.

In-app navigation (Deep Links)

Buttons, SwipeUp gestures, and clickable images can navigate directly inside your app (e.g. to a specific product screen). Set the appUrl field in the Storyfyd creator. When tapped, the SDK invokes storyfydShouldHandleURL(_:) on your delegate before falling back to opening an external browser.

  • Register your app's custom URL scheme in the dashboard: Settings → SDK → App URL Scheme.
  • Use this scheme in the appUrl field in the story creator (e.g. yourscheme://product?id=42).
  • Return true from storyfydShouldHandleURL to handle navigation yourself — the viewer closes automatically, and you perform navigation inside storyfydDidCloseViewer().
AppDeepLinkHandler.swift
final class AppDeepLinkHandler: StoryfydDelegate {
var pendingRoute: AppRoute?
private var routeAfterClose: AppRoute?
func storyfydShouldHandleURL(_ url: URL) -> Bool {
guard let route = AppRoute(url: url) else { return false }
routeAfterClose = route
return true // viewer zamknie się automatycznie
}
// Wywoływane po zakończeniu animacji zamknięcia viewera
func storyfydDidCloseViewer() {
guard let route = routeAfterClose else { return }
pendingRoute = route
routeAfterClose = nil
}
}

Integration with UIKit applications

StoryfydDelegate is a standard Swift protocol — it works identically in UIKit projects. Embed the stories view by wrapping it in a UIHostingController, and handle deep links exactly as you would in SwiftUI.

MyViewController.swift
class MyDeepLinkHandler: StoryfydDelegate {
weak var navigationController: UINavigationController?
func storyfydShouldHandleURL(_ url: URL) -> Bool {
guard url.scheme == "yourscheme" else { return false }
return true // handle recognised scheme, let SDK close the viewer
}
func storyfydDidCloseViewer() {
guard ... else { return }
// navigate after the viewer is fully dismissed
let vc = ProductViewController()
navigationController?.pushViewController(vc, animated: true)
}
}
// In your UIViewController:
let handler = MyDeepLinkHandler()
handler.navigationController = navigationController
let storiesView = StoryfydStoriesView(delegate: handler)
let hostingVC = UIHostingController(rootView: storiesView)
hostingVC.view.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 120)
addChild(hostingVC)
view.addSubview(hostingVC.view)
hostingVC.didMove(toParent: self)

Deep link execution flow

  • User taps a CTA element configured with appUrl: "yourscheme://product?id=42".
  • The SDK calls storyfydShouldHandleURL(_:) → your delegate returns true.
  • The story viewer smoothly closes.
  • storyfydDidCloseViewer() is called.
  • Your app transitions to the destination screen (e.g. product details or checkout).

Troubleshooting (FAQ)

Common scenarios encountered when integrating the SDK and how to resolve them quickly.

Stories are not loading

Common causes: missing or invalid API token, wrong API host, or CORS restrictions. Inspect the browser console and Network tab for HTTP 401/403 errors or blocked cross-origin requests.

Widget does not render in React

Make sure the web script is loaded before the React component mounts, your token is valid, and no CORS errors appear in the console.

Background video does not autoplay

Modern browsers permit video autoplay only when muted — the SDK sets this automatically. Also verify that your video uses MP4 (H.264) or WebM format and that CORS headers on your asset server permit streaming.

Props do not update dynamically in React

The widget initializes once on mount. To force re-initialization when the token or user changes, pass a dynamic key prop: <StoryfydWidget key={token} />.