Rewind Reels Docscontact@purposeforce.org

API Reference

Last updated March 2026

On this page

  1. Overview
  2. Authentication
  3. Rate Limits
  4. Architecture
  5. Health
  6. Registration
  7. License Check
  8. Pipeline: Generate
  9. Player: Get Video
  10. Player: Record View
  11. Player: Record Completion
  12. OG Image
  13. Billing: Checkout
  14. Billing: Webhook
  15. Billing: Portal
  16. Admin: List Licenses
  17. Admin: Override License

Overview

The Rewind Reels API powers the video generation pipeline, public player, billing, and administration features. The base URL is https://api.rewind.purposeforce.org. All endpoints accept and return JSON unless otherwise noted. Errors follow a consistent shape:

{ "error": "Human-readable error message" }

Authentication

Three authentication methods are used depending on the endpoint category:

MethodHeaderUsed By
API Keyx-rewind-api-key: rw_00D5f000000XXXX_hmac...Pipeline endpoints (generate)
Basic Auth (Admin)x-admin-key: <REWIND_ADMIN_KEY>Admin endpoints (licenses, override)
None (Public)(no auth header)Health, registration, player, billing checkout/portal, OG image
Important: API keys are generated during registration and stored on the Salesforce Rewind_License__c.Api_Key__c field. Never expose API keys in client-side code. All API calls originate from Apex server-side code.

Rate Limits

Rate limiting prevents abuse and ensures fair usage across all orgs.

Endpoint CategoryLimitKey
Pipeline (authenticated)30 requests / minuteAPI key prefix (first 16 chars)
Player (public)30 requests / minuteClient IP address
License check30 requests / minuteClient IP address
Billing portal30 requests / minuteClient IP address
Admin override30 requests / minuteClient IP address

When rate-limited, endpoints return 429 Too Many Requests.

Architecture

The Rewind Reels pipeline is orchestrated from the Salesforce org. The LWC Dashboard triggers Apex, which calls the backend API in sequence. Here is the end-to-end flow:

Salesforce Org
  |-- LWC Dashboard --> Apex Controller
       |-- RewindGenerateQueueable (sends sessionToken + shareToken)
            |
            `-- HTTP POST --> /api/pipeline/generate
                     |-- Claude AI (Anthropic) generates narration + theme
                     |-- Caches narration + theme in Vercel Blob (keyed by shareToken)
                     |-- Callbacks to SF using sessionToken (stores on Rewind_Video__c)
                     `-- Returns: narration JSON, theme, status

Public Player (rewind.purposeforce.org/v/:token)
  |-- GET /api/player/:token
  |        |-- Checks Vercel Blob cache first (fast, no SF query)
  |        |-- Falls back to Salesforce SOQL if not cached
  |        `-- Returns JSON to client
  |
  `-- Client-side animated HTML player
           |-- Renders scenes with spring animations, cross-fades
           |-- Plays background music via Web Audio API
           `-- Tracks views + completion via POST calls

The public player (rewind.purposeforce.org/v/:token) fetches narration JSON and theme data via the player API. Data is served from Vercel Blob cache when available, falling back to a Salesforce SOQL query. No video files are generated — the player renders scenes with spring-driven animations, cross-fades, and optional background music. View and completion analytics are tracked with fire-and-forget POST calls.

Health Check

GET/api/health

Simple liveness probe. No authentication required. Response is cached for 60 seconds.

Response

{
  "status": "ok",
  "version": "12"
}
StatusDescription
200Service is healthy

Registration

POST/api/register

Registers a new Salesforce org and provisions an API key. Always assigns the free tier regardless of any tier value sent in the request body. Tier upgrades must go through the billing checkout flow.

Request Body

{
  "orgId": "00D5f000000XXXX",     // Required. 15 or 18 char Salesforce org ID
  "orgType": "production"          // Optional. Informational only
}

Response

{
  "apiKey": "rw_00D5f000000XXXX_a1b2c3d4e5f6...",
  "tier": "free",
  "rendersLimit": 3,
  "verification": "sha256-hash-string",
  "licenseToken": "base64url-payload.base64url-signature"
}
StatusDescription
200Registration successful
400Missing or invalid orgId
500Registration failed
503Server configuration error (missing secrets)

License Check

POST/api/license/check

Called by the Salesforce org to check the current tier (e.g., after a billing upgrade). Looks up the Stripe customer by API key and returns the current tier and a fresh license token.

Request Body

{
  "apiKey": "rw_00D5f000000XXXX_a1b2c3...",   // Required
  "orgId": "00D5f000000XXXX"               // Required
}

Response

{
  "tier": "professional",
  "rendersLimit": 75,
  "licenseToken": "base64url-payload.base64url-signature"
}
StatusDescription
200License check successful. Returns free tier if no Stripe customer found.
400Missing apiKey or orgId, or invalid API key format
429Rate limit exceeded
500License check failed

Pipeline: Narrate

POST/api/pipeline/narrate

Generates a narration script from Salesforce data using Claude AI (Anthropic). This is step 1 of the pipeline. Requires API key authentication. Max execution time: 60 seconds.

Request Body

{
  "data": {
    "records": [ ... ],          // Required. Array of Salesforce records
    "recordCount": 42,
    "objectLabel": "Opportunity",
    "aggregates": { ... }        // Optional. Pre-computed aggregates
  },
  "presetType": "sales_cloud",  // Preset template identifier
  "tone": "professional",       // professional | motivational | analytical
  "audience": "Sales Team",
  "customPrompt": "Focus on deals closing this week",
  "periodLabel": "Week of March 3",
  "videoLength": "standard"      // quick | standard | deep_dive
}

Response

{
  "narration": {
    "title": "Sales Pipeline Recap — Week of March 3",
    "scenes": [
      {
        "type": "intro",
        "headline": "Your Weekly Pipeline",
        "body": "Let's look at how the team performed..."
      },
      {
        "type": "metric",
        "headline": "$2.4M in Pipeline",
        "body": "Pipeline grew 12% week over week...",
        "metric": { "value": "$2.4M", "label": "Total Pipeline" }
      }
    ]
  },
  "narrationTitle": "Sales Pipeline Recap — Week of March 3"
}
StatusDescription
200Narration generated successfully
400No data provided (empty records array)
401Invalid or missing API key
429Rate limit exceeded
500Narration generation failed

Pipeline: Generate

POST/api/pipeline/generate

Combined generation route — narrates data with Claude AI, builds the player theme, caches the result in Vercel Blob, and callbacks to Salesforce with narration JSON + theme using the provided session token. The resulting narration JSON is rendered client-side as an animated HTML player. Max execution time: 120 seconds.

Request Body

Same as the Narrate route, plus:

{
  "videoId": "a0x5f000001XXXX",       // Required. Rewind_Video__c record ID
  "instanceUrl": "https://your-instance.my.salesforce.com",  // Salesforce org instance URL
  "sessionToken": "00D...!AQ...",     // Session token from Apex (for callback auth)
  "shareToken": "a1b2c3d4e5f6...",    // 32-char hex token (for Vercel Blob caching)
  "data": { ... },                     // Required. Same format as narrate
  "brand": {                           // Optional. Branding overrides
    "primaryColor": "#7C3AED",
    "secondaryColor": "#F43F5E",
    "logoUrl": "https://...",
    "backgroundMusic": "none"          // none | upbeat | corporate | chill
  },
  "presetType": "sales_cloud",
  "videoLength": "standard"            // quick | standard | deep_dive
}

Response

{
  "success": true,
  "narration": { "title": "...", "scenes": [...] },
  "narrationTitle": "Sales Pipeline Recap — Q1 2026",
  "theme": { "colors": {...}, "font": {...}, ... }
}
StatusDescription
200Narration generated and callback sent to Salesforce
400No data provided or missing videoId
401Invalid or missing API key
422Narration produced no scenes
429Rate limit exceeded
500Generation failed

Player: Get Video

GET/api/player/[token]

Public endpoint for the animated HTML player. Fetches narration JSON and theme from Vercel Blob cache (fast) or Salesforce (fallback) using the share token. No authentication required. Tokens are 32-character hex strings (128-bit cryptographic randomness). Legacy videos without narration JSON return a videoUrl fallback.

Response (Animated Player)

{
  "title": "Sales Pipeline Recap — Q1 2026",
  "narration": { "title": "...", "scenes": [...] },
  "theme": { "colors": {...}, "font": {...} },
  "status": "complete",
  "whiteLabel": false
}

Response (Legacy Video)

{
  "title": "Old Video Title",
  "narration": null,
  "theme": null,
  "videoUrl": "https://example.com/video.mp4",
  "legacy": true,
  "status": "complete",
  "whiteLabel": false
}
StatusDescription
200Video metadata returned
400Invalid token format (must be 32 hex chars)
404Video not found
429Rate limit exceeded
500Unable to load video

Player: Record View

POST/api/player/[token]/view

Increments the view count on a Rewind_Video__c record. Called by the player when a video starts playing. Fire-and-forget — always returns 200 even on internal errors so the player experience is unaffected.

Request Body

Empty body or no body required.

Response

{ "ok": true }
StatusDescription
200Always returned (check ok field for actual success)
429Rate limit exceeded

Player: Record Completion

POST/api/player/[token]/complete

Records watch analytics when a viewer finishes (or leaves) a video. Updates watch time, completion rate, and viewer country on the Rewind_Video__c record. Country is detected from Vercel's x-vercel-ip-country header.

Request Body

{
  "watchTimeSeconds": 45,    // Optional. Seconds watched
  "completionRate": 87       // Optional. 0-100 percentage
}

Response

{ "ok": true }
StatusDescription
200Always returned (check ok field for actual success)
429Rate limit exceeded

OG Image

GET/api/og/[token]

Generates an Open Graph image (1200x630) for social media link previews. Runs on the Edge runtime. Fetches the video title from Salesforce and renders a branded preview card.

Response

Returns a image/png response (not JSON). Used by the player page's meta tags.

StatusDescription
200PNG image returned

Billing: Checkout

POST/api/billing/checkout

Creates a Stripe Checkout session for upgrading to a paid tier. Returns a redirect URL to the Stripe-hosted checkout page. Supports monthly and annual billing intervals. Promotion codes are enabled.

Request Body

{
  "tier": "professional",       // Required. starter | professional | enterprise
  "interval": "monthly",        // Optional. monthly (default) | annual
  "orgId": "00D5f000000XXXX",   // Required. Salesforce org ID
  "email": "admin@example.com", // Optional. Pre-fills checkout email
  "apiKey": "rw_00D5f000000XXXX_abc123..." // Required. Current API key
}

Response

{
  "url": "https://checkout.stripe.com/c/pay/cs_live_..."
}
StatusDescription
200Checkout session created
400Invalid tier, missing orgId, missing apiKey, or invalid interval
500Checkout failed

Billing: Webhook

POST/api/billing/webhook

Receives Stripe webhook events. Verified using the stripe-signature header and the STRIPE_WEBHOOK_SECRET environment variable. On successful events, syncs the updated tier and render limits back to the Salesforce Rewind_License__c record.

Handled Events

EventAction
checkout.session.completedUpgrades the license tier in Salesforce, updates customer metadata in Stripe
customer.subscription.updatedSyncs the new tier from the subscription price to Salesforce
customer.subscription.deletedDowngrades the license to free tier in Salesforce
Note: The webhook always returns 200 to prevent Stripe from retrying endlessly, even if the handler encounters errors.
StatusDescription
200Event received and processed (or gracefully failed)
400Missing stripe-signature header or invalid signature

Billing: Portal

POST/api/billing/portal

Creates a Stripe Customer Portal session for managing an existing subscription (cancel, update payment method, view invoices). Looks up the Stripe customer by Salesforce org ID.

Request Body

{
  "orgId": "00D5f000000XXXX"   // Required. Salesforce org ID
}

Response

{
  "url": "https://billing.stripe.com/p/session/..."
}
StatusDescription
200Portal session created
400Missing or invalid orgId
404No billing account found for this org
429Rate limit exceeded
500Portal session failed

Admin: List Licenses

GET/api/admin/licenses

Lists all Rewind License records from Salesforce. Requires admin authentication via the x-admin-key header. API keys are truncated in the response (first 12 characters only).

Response

{
  "licenses": [
    {
      "id": "a0x5f000001XXXX",
      "name": "RL-00001",
      "apiKey": "rw_00D5f00_a1b2...",
      "tier": "professional",
      "isActive": true,
      "rendersUsed": 12,
      "rendersLimit": 75,
      "nonprofitDiscount": false,
      "setupComplete": true,
      "created": "2026-01-15T10:00:00.000+0000",
      "modified": "2026-03-01T14:30:00.000+0000"
    }
  ],
  "total": 1
}
StatusDescription
200License list returned
401Invalid or missing admin key
500Failed to list licenses

Admin: Override License

POST/api/admin/override

Directly overrides a license tier without requiring a Stripe payment. Use for free upgrades, discounts, or revoking access. Requires admin authentication.

Request Body

{
  "apiKey": "rw_00D5f00_a1b2c3d4...",   // Required. Target license API key
  "orgId": "00D5f000000XXXX",         // Optional. Falls back to stored org ID
  "tier": "enterprise",               // Optional. Defaults to "free"
  "rendersLimit": 9999,               // Optional. Defaults to tier's standard limit
  "isActive": true,                    // Optional. Enable/disable the license
  "reason": "Partner comp"             // Optional. Audit note (logged server-side)
}

Response

{
  "success": true,
  "licenseId": "a0x5f000001XXXX",
  "previousTier": "free",
  "newTier": "enterprise",
  "rendersLimit": 9999,
  "reason": "Partner comp"
}
StatusDescription
200Override applied
400Missing apiKey, invalid API key format, or invalid tier
401Invalid or missing admin key
404License not found for the given API key
429Rate limit exceeded
500Override failed

Tier Configuration

These are the render limits enforced by the API per billing tier:

TierRenders / MonthNotes
free3Default tier for all new registrations
starter15Scheduling + custom branding
professional75Email delivery + up to 10 concurrent
enterpriseUnlimited (9999)White-label player + analytics dashboard
Need help integrating? Email contact@purposeforce.org or visit the documentation index.