Documentation
Everything you need to integrate RecapKit into your application.
Quick Start
Pass your API key directly to renderStory() and you're done — the SDK auto-initializes for you. For advanced use, you can call init() separately to inspect your plan limits before rendering. Choose your integration path below.
UMD Usage (Script Tag)
Include the UMD bundle via a script tag. React is bundled in and CSS is auto-injected — no build step required.
<!DOCTYPE html>
<html>
<head>
<title>My Recap</title>
</head>
<body>
<div id="recap-container"></div>
<script src="https://cdn.recapkit.app/v1/recapkit.umd.js"
integrity="sha384-NGjdeLhzl3MdOXG4mjq1YbJA7/dQsomAVhUy6j4A0p1IhASSHv7zuWYpEXAw7L3c"
crossorigin="anonymous"></script>
<script>
async function showRecap() {
const instance = await RecapKit.renderStory({
apiKey: 'rk_live_your_api_key_here',
elementId: 'recap-container',
storyData: {
slides: [
{ title: 'Welcome!', description: 'Your recap' },
{ type: 'stat', title: 'Metrics', stats: { 'Users': 1200 } },
{ type: 'summary', title: "That's a Wrap!" }
]
},
displayMode: 'embedded'
})
}
showRecap()
</script>
</body>
</html> Note: The global RecapKit object exposes init, renderStory, reset, and RecapKitError.
Installation (ES Module)
Install RecapKit from npm:
npm install recapkit Tip: RecapKit has peer dependencies on react and react-dom (version 18 or 19). Make sure they are installed in your project.
ESM Usage
Import the SDK, initialize with your API key, then render a story into a DOM element.
import { renderStory, RecapKitError } from 'recapkit'
async function showRecap() {
try {
const instance = await renderStory({
apiKey: 'rk_live_your_api_key_here',
elementId: 'recap-container',
storyData: {
slides: [
{ title: 'Welcome!', description: 'Your quarterly recap' },
{ type: 'stat', title: 'Your Activity', stats: { 'Tasks Completed': 42, 'Projects Launched': 5 } },
{ type: 'summary', title: "That's a Wrap!", description: 'Keep up the great work!' }
]
},
displayMode: 'overlay',
onClose: () => console.log('Story closed')
})
// Clean up when done
instance.destroy()
} catch (err) {
if (err instanceof RecapKitError) {
console.error(`RecapKit error [${err.code}]: ${err.message}`)
if (err.code === 'VALIDATION_ERROR') {
console.error('Validation details:', err.errors)
}
}
}
}
showRecap() Tip: Make sure a <div id="recap-container"></div> exists in your HTML before calling renderStory().
Story Data Format
The storyData object describes your recap content. Here is a full example:
{
"slides": [
{
"title": "Welcome!",
"description": "Your quarterly recap"
},
{
"type": "stat",
"title": "Your Activity",
"stats": { "Tasks Completed": 42, "Projects Launched": 5 }
},
{
"type": "image",
"title": "Top Moment",
"description": "Your most productive week",
"imageUrl": "https://example.com/hero.jpg"
},
{
"type": "summary",
"title": "That's a Wrap!",
"description": "Keep up the great work!"
}
],
"theme": {
"colors": {
"primary": "#10B981",
"secondary": "#3B82F6"
}
},
"displayMode": "embedded",
"showNavButtons": true
} ExternalStoryData Properties
| Property | Type | Required | Description |
|---|---|---|---|
version | string | No | Schema version — defaults to "1.0.0" |
slides | SlideData[] | Yes | Array of slides (minimum 1) |
theme | StoryTheme | No | Theme customization (see Theme) |
displayMode | DisplayMode | No | Overridable by renderStory() options |
showNavButtons | boolean | No | Overridable by renderStory() options |
Slide Types
Each slide requires a type field that controls its layout and animations. Depending on the type, additional fields may be required. Four types are available:
default
General content slide. Solid background with optional image backdrop. Supports title, description, and simple stats.
stat
Gradient background (secondary to primary). Animated glassmorphism stat cards. Best with 1-3 stat entries.
image
Full-bleed background image with dark overlay and Ken Burns zoom effect. Large text overlay for maximum impact.
summary
Three-color gradient (secondary to primary to pink) with sparkle decoration. Ideal for closing slides.
Slide Type Definitions
// Each slide requires a 'type' field.
// Depending on the type, additional fields may be required.
// Stat slides — 'stats' is required
interface StatSlideData {
type: 'stat'
id?: string
title?: string
description?: string
stats: Record<string, string | number> // Required
}
// Image slides — 'imageUrl' is required
interface ImageSlideData {
type: 'image'
id?: string
title?: string
description?: string
imageUrl: string // Required
}
// Summary slides
interface SummarySlideData {
type: 'summary'
id?: string
title?: string
description?: string
}
// Default slides
interface DefaultSlideData {
type: 'default'
id?: string
title?: string
description?: string
imageUrl?: string
stats?: Record<string, string | number>
}
// Union of all slide types
type SlideData = StatSlideData | ImageSlideData | SummarySlideData | DefaultSlideData Theme Configuration
Customize the visual appearance by passing a theme object in your story data. All properties are optional — defaults are applied for any omitted values.
{
"theme": {
"colors": {
"primary": "#10B981",
"secondary": "#3B82F6",
"background": "#000000",
"text": "#FFFFFF"
},
"fontFamily": "Inter, sans-serif",
"borderRadius": "1rem"
}
} Defaults
| Property | Default | Description |
|---|---|---|
colors.primary | #10B981 | Primary accent color |
colors.secondary | #3B82F6 | Secondary/gradient color |
colors.background | #000000 | Slide background color |
colors.text | #FFFFFF | Text color |
fontFamily | Inter, sans-serif | Font stack |
borderRadius | 1rem | Container border radius (0 in fullscreen mode) |
Display Modes
Control how the story is presented using the displayMode option.
embedded
Default
9:16 aspect ratio, max 448px wide. Uses the themed border-radius. Renders inline within your page layout.
fullscreen
Fills the entire viewport. No border-radius. Scroll is locked while active.
overlay
Modal with dark backdrop and spring animation. Closable via Escape key or backdrop click.
Tip: For fullscreen and overlay modes, pass an onClose callback to handle cleanup when the user dismisses the story.
API Reference
init(config)
Validates your API key against the RecapKit backend. Optional if you pass apiKey to renderStory(), which auto-initializes the SDK.
function init(config: RecapKitConfig): Promise<RecapKitInitResult> | Parameter | Type | Description |
|---|---|---|
config.apiKey | string | Your RecapKit API key (rk_live_... or rk_test_...) |
Returns: Promise<RecapKitInitResult> — an object with isTestKey (boolean) and limits (recapLimit, recapsUsed, recapsRemaining).
Throws: RecapKitError with code INVALID_KEY, KEY_EXPIRED, KEY_REVOKED, or NETWORK_ERROR.
renderStory(options)
Renders a story into the specified DOM element. Tracks usage per call.
function renderStory(options: RecapKitOptions): Promise<RecapKitInstance> | Parameter | Type | Required | Description |
|---|---|---|---|
elementId | string | Yes | ID of the target DOM element |
storyData | ExternalStoryData | Yes | Story content and configuration |
apiKey | string | No | API key — if provided and SDK not yet initialized, auto-calls init(). Alternative to calling init() separately. |
displayMode | DisplayMode | No | Overrides storyData.displayMode (default: 'embedded') |
showNavButtons | boolean | No | Overrides storyData.showNavButtons (default: true) |
onClose | () => void | No | Called when the story is dismissed (fullscreen/overlay) |
Returns: Promise<RecapKitInstance> — an object with a destroy() method to unmount the story and free resources.
Throws: RecapKitError with code NOT_INITIALIZED, VALIDATION_ERROR, LIMIT_EXCEEDED, or NETWORK_ERROR.
reset()
Clears the internal SDK configuration state. Use for SPA cleanup or to re-initialize with a different API key.
function reset(): void Error Handling
Both init() and renderStory() throw RecapKitError with a code property for programmatic handling.
Error Codes
| Code | Thrown by | Description |
|---|---|---|
INVALID_KEY | init() | API key is not valid |
KEY_EXPIRED | init() | API key has expired |
KEY_REVOKED | init() | API key has been revoked |
LIMIT_EXCEEDED | renderStory() | Monthly recap limit reached |
NOT_INITIALIZED | renderStory() | init() was not called first |
VALIDATION_ERROR | renderStory() | Story data failed validation — check err.errors for details |
NETWORK_ERROR | Both | Cannot reach the RecapKit API |
import { renderStory, RecapKitError } from 'recapkit'
try {
await renderStory({ apiKey: 'rk_live_...', elementId: 'recap', storyData })
} catch (err) {
if (err instanceof RecapKitError) {
switch (err.code) {
case 'INVALID_KEY':
case 'KEY_EXPIRED':
case 'KEY_REVOKED':
console.error('API key issue:', err.message)
break
case 'LIMIT_EXCEEDED':
console.warn('Recap limit reached for this billing period')
break
case 'NOT_INITIALIZED':
console.error('Call init() or pass apiKey to renderStory()')
break
case 'VALIDATION_ERROR':
console.error('Invalid story data:', err.errors)
break
case 'NETWORK_ERROR':
console.error('Network error — retrying may help')
break
}
}
} Tip: Test keys (rk_test_...) work the same as live keys but display a dev watermark on the story. Use them during development to avoid consuming your monthly recap quota.
TypeScript Types
All types are exported from the recapkit package:
import type {
ExternalStoryData,
SlideData,
StoryTheme,
DisplayMode,
RecapKitErrorCode,
RecapKitConfig,
RecapKitOptions,
RecapKitInstance,
RecapKitInitResult
} from 'recapkit' Full Type Definitions
// --- Configuration ---
interface RecapKitConfig {
apiKey: string
}
interface RecapKitInitResult {
isTestKey: boolean
limits: {
recapLimit: number
recapsUsed: number
recapsRemaining: number
}
}
// --- Render Options ---
interface RecapKitOptions {
elementId: string
storyData: ExternalStoryData
apiKey?: string
displayMode?: DisplayMode
showNavButtons?: boolean
onClose?: () => void
}
interface RecapKitInstance {
destroy: () => void
}
// --- Story Data ---
interface ExternalStoryData {
version?: string
slides: SlideData[]
theme?: StoryTheme
displayMode?: DisplayMode
showNavButtons?: boolean
}
// Slide types — 'type' is required on every slide
interface StatSlideData {
type: 'stat'
id?: string
title?: string
description?: string
stats: Record<string, string | number>
}
interface ImageSlideData {
type: 'image'
id?: string
title?: string
description?: string
imageUrl: string
}
interface SummarySlideData {
type: 'summary'
id?: string
title?: string
description?: string
}
interface DefaultSlideData {
type: 'default'
id?: string
title?: string
description?: string
imageUrl?: string
stats?: Record<string, string | number>
}
type SlideData = StatSlideData | ImageSlideData | SummarySlideData | DefaultSlideData
// --- Theme ---
interface StoryTheme {
colors?: {
primary?: string
secondary?: string
background?: string
text?: string
}
fontFamily?: string
borderRadius?: string
}
type DisplayMode = 'embedded' | 'fullscreen' | 'overlay'
// --- Errors ---
type RecapKitErrorCode =
| 'INVALID_KEY'
| 'KEY_EXPIRED'
| 'KEY_REVOKED'
| 'LIMIT_EXCEEDED'
| 'NOT_INITIALIZED'
| 'VALIDATION_ERROR'
| 'NETWORK_ERROR'
class RecapKitError extends Error {
code: RecapKitErrorCode
errors?: string[]
constructor(code: RecapKitErrorCode, message: string, errors?: string[])
} Need help with integration? We're here to help.