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.

1

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.

2

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.

3

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().

4

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
5

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
6

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)
7

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.

8

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
9

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.

10

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.