<div align="center"> <a href="https://boldvideo.com?utm_source=github.com&utm_medium=readme&utm_campaign=bold-js" align="center"> <img src="https://boldvideo.com/bold-js-github-header.svg" alt="Bold Logo"> </a> <h1 align="center rainbow">@boldvid
npm install @boldvideo/bold-js
The JavaScript SDK for interacting with the Bold API, to power your own business video platform.
---
``bash`
npm install @boldvideo/bold-js
`typescript
import { createClient } from '@boldvideo/bold-js';
const bold = createClient('your-api-key');
// Fetch videos
const videos = await bold.videos.list();
// AI-powered recommendations
const recs = await bold.ai.recommendations({
topics: ['sales', 'negotiation'],
stream: false
});
console.log(recs.guidance);
`
---
`typescript
// List latest videos (default: 12)
const videos = await bold.videos.list();
// With limit (backwards compatible)
const videos = await bold.videos.list(20);
// With filters
const videos = await bold.videos.list({
limit: 20,
tag: 'sales',
collectionId: 'col_123',
viewerId: 'viewer_123' // Include watch progress
});
// Paginated index (uses /videos endpoint)
const videos = await bold.videos.list({
page: 2,
tag: 'sales',
collectionId: 'col_123'
});
// Get a single video by ID or slug
const video = await bold.videos.get('video-id');
const videoBySlug = await bold.videos.get('my-video-slug');
// Search videos
const results = await bold.videos.search('pricing strategies');
`
`typescript
// List all playlists
const playlists = await bold.playlists.list();
// Get a single playlist with videos
const playlist = await bold.playlists.get('playlist-id');
`
`typescript
// Fetch channel settings, menus, and featured playlists
const settings = await bold.settings();
// Access portal hero configuration
if (settings.portal.hero.type === 'custom') {
// Render custom hero section
}
// Menu items with external link handling
settings.menuItems.forEach(item => {
// item.blank: true opens in a new window/tab
// item.isExt: true indicates an external URL
// item.icon: optional icon path (can be null)
});
`
---
Manage external users and track their video watch progress. Ideal for course platforms integrating with Bold Video.
`typescript
// Create a viewer (e.g., when user signs up)
const { data: viewer } = await bold.viewers.create({
name: 'John Doe',
externalId: 'user_123', // Your platform's user ID
email: 'john@example.com',
traits: { plan: 'pro', company_name: 'Acme Inc' }
});
// Find viewer by external ID (common for syncing users)
const { data: viewer } = await bold.viewers.lookup({ externalId: 'user_123' });
// Or find by email
const { data: viewer } = await bold.viewers.lookup({ email: 'john@example.com' });
// Update viewer
await bold.viewers.update(viewer.id, {
traits: { plan: 'enterprise' } // Note: traits are replaced, not merged
});
// List all viewers
const { data: viewers } = await bold.viewers.list();
`
`typescript
// Save progress as video plays (call every 5-10 seconds)
await bold.viewers.saveProgress(viewerId, videoId, {
currentTime: 120, // seconds
duration: 600 // total video duration
});
// Mark video complete by setting currentTime = duration
await bold.viewers.saveProgress(viewerId, videoId, {
currentTime: 600,
duration: 600
});
// Get progress for a specific video
const { data: progress } = await bold.viewers.getProgress(viewerId, videoId);
console.log(${progress.percentage}% complete);
// List all progress for a viewer (e.g., for a course dashboard)
const { data: progress, meta } = await bold.viewers.listProgress(viewerId, {
collectionId: 'course-collection-id', // Filter to a course
completed: false // Only in-progress videos
});
console.log(Completed ${meta.completed} of ${meta.total} videos);`
---
Build community features with posts, comments, and reactions. All write operations require a viewerId (the viewer performing the action).
`typescript
// List posts (optionally filter by category)
const { data: posts } = await bold.community.posts.list({
category: 'announcements',
limit: 20,
offset: 0,
viewerId: 'viewer-uuid' // Include viewerReacted in response
});
// Get a single post with comments
const { data: post } = await bold.community.posts.get('post-id', 'viewer-uuid');
// Create a post (requires viewerId)
const { data: newPost } = await bold.community.posts.create('viewer-uuid', {
content: 'Hello community! Markdown supported.',
category: 'general'
});
// Update a post (owner or admin only)
await bold.community.posts.update('viewer-uuid', 'post-id', {
content: 'Updated content'
});
// Delete a post (owner or admin only)
await bold.community.posts.delete('viewer-uuid', 'post-id');
// React to a post (toggle like/unlike)
const reaction = await bold.community.posts.react('viewer-uuid', 'post-id');
console.log(reaction.reacted, reaction.reactionsCount);
`
`typescript
// Create a comment on a post
const { data: comment } = await bold.community.comments.create(
'viewer-uuid',
'post-id',
{ content: 'Great post!' }
);
// Reply to a comment (nested)
const { data: reply } = await bold.community.comments.create(
'viewer-uuid',
'post-id',
{ content: 'I agree!', parentId: 'parent-comment-id' }
);
// Delete a comment (owner or admin only)
await bold.community.comments.delete('viewer-uuid', 'comment-id');
// React to a comment (toggle)
const reaction = await bold.community.comments.react('viewer-uuid', 'comment-id');
`
---
All AI methods support both streaming (default) and non-streaming modes.
Library-wide conversational AI for deep Q&A across your entire video library.
`typescript
// Streaming (default)
const stream = await bold.ai.chat({ prompt: 'How do I price my SaaS?' });
for await (const event of stream) {
if (event.type === 'text_delta') process.stdout.write(event.delta);
if (event.type === 'sources') console.log('Sources:', event.sources);
}
// Non-streaming
const response = await bold.ai.chat({
prompt: 'What are the best closing techniques?',
stream: false
});
console.log(response.content);
`
Options:
| Parameter | Type | Description |
|-----------|------|-------------|
| prompt | string | The user's question (required) |stream
| | boolean | true (default) for SSE, false for JSON |videoId
| | string | If provided, scope to this video instead of whole library |currentTime
| | number | Current playback position (only with videoId) |conversationId
| | string | Pass to continue existing conversation |collectionId
| | string | Filter to a specific collection |tags
| | string[] | Filter by tags |
Get AI-powered video recommendations based on topics — ideal for personalized learning paths, exam prep, and content discovery.
`typescript
// Streaming (default)
const stream = await bold.ai.recommendations({
topics: ['contract law', 'ethics', 'client management'],
});
for await (const event of stream) {
if (event.type === 'recommendations') {
event.recommendations.forEach(rec => {
console.log(${rec.topic}:); - ${v.title} (${v.relevance})
rec.videos.forEach(v => console.log());
});
}
if (event.type === 'text_delta') {
process.stdout.write(event.delta); // AI guidance
}
}
// Non-streaming
const response = await bold.ai.recommendations({
topics: ['sales', 'marketing'],
stream: false
});
console.log(response.guidance);
console.log(response.recommendations);
`
Options:
| Parameter | Type | Description |
|-----------|------|-------------|
| topics | string[] | Topics to find content for (required) |stream
| | boolean | true (default) for SSE, false for JSON |limit
| | number | Max videos per topic (default: 5, max: 20) |collectionId
| | string | Filter to a specific collection |tags
| | string[] | Filter by tags |includeGuidance
| | boolean | Include AI learning path narrative (default: true) |context
| | AIContextMessage[] | Previous conversation turns for follow-ups |
Fast semantic search with a brief AI-generated summary.
`typescript
const stream = await bold.ai.search({
prompt: 'pricing strategies',
limit: 10
});
for await (const event of stream) {
if (event.type === 'sources') {
console.log(Found ${event.sources.length} results);`
}
}
Chat about a specific video by passing videoId. Uses only that video's transcript as context.
`typescript
const stream = await bold.ai.chat({
videoId: 'video-id',
prompt: 'What is discussed at the 5 minute mark?'
});
for await (const event of stream) {
if (event.type === 'text_delta') process.stdout.write(event.delta);
}
// With playback context (coming soon)
const stream = await bold.ai.chat({
videoId: 'video-id',
prompt: 'What does she mean by that?',
currentTime: 847 // seconds
});
`
Retrieve a conversation by ID to display message history:
`typescript
const conversation = await bold.ai.getConversation('550e8400-e29b-41d4-a716-446655440000');
console.log(Created: ${conversation.createdAt});${msg.role}: ${msg.content}
for (const msg of conversation.messages) {
console.log();`
}
Use the context parameter for follow-up questions:
`typescript
const first = await bold.ai.search({
prompt: 'How do indie designers find clients?',
stream: false
});
const followUp = await bold.ai.search({
prompt: 'What about cold outreach specifically?',
context: first.context,
stream: false
});
`
---
Track video events and page views for analytics.
`typescript
// Track video events (play, pause, complete, etc.)
bold.trackEvent({
type: 'play',
videoId: 'video-id',
timestamp: 0
});
// Track page views
bold.trackPageView({
path: '/videos/my-video',
referrer: document.referrer
});
`
---
All types are exported for full TypeScript support:
`typescript`
import type {
Video,
Playlist,
Settings,
Portal,
PortalHero,
MenuItem,
AIEvent,
AIResponse,
ChatOptions,
SearchOptions,
RecommendationsOptions,
RecommendationsResponse,
Recommendation,
Conversation,
ConversationMessage,
Source,
Viewer,
ViewerProgress,
ViewerLookupParams,
ListProgressOptions,
ListVideosOptions,
ListVideosLatestOptions,
ListVideosIndexOptions,
// Community API
Post,
PostAuthor,
Comment,
ReactionResponse,
ListPostsOptions,
CreatePostData,
UpdatePostData,
CreateCommentData
} from '@boldvideo/bold-js';
---
All API responses (videos, playlists, settings, AI) are now transformed to use idiomatic TypeScript/JavaScript naming:
`typescript
// Before (v1.7.x and earlier)
video.playback_id
video.published_at
video.stream_url
video.meta_data
settings.featured_playlists
settings.menu_items
settings.theme_config
playlist.is_private
// After (v1.8.0)
video.playbackId
video.publishedAt
video.streamUrl
video.metaData
settings.featuredPlaylists
settings.menuItems
settings.themeConfig
playlist.isPrivate
`
| Old | New | Notes |
|-----|-----|-------|
| bold.ai.ask(opts) | bold.ai.chat(opts) | ask() still works but is deprecated |bold.ai.coach(opts)
| | bold.ai.chat(opts) | coach() still works but is deprecated |bold.ai.chat(videoId, opts)
| | bold.ai.chat({ videoId, ...opts }) | Pass videoId in options |bold.ai.recommend(opts)
| | bold.ai.recommendations(opts) | recommend() still works but is deprecated |
| Old Type | New Type |
|----------|----------|
| AskOptions | ChatOptions |RecommendOptions
| | RecommendationsOptions |RecommendResponse
| | RecommendationsResponse` |
The old types are still exported as aliases for backward compatibility.
---
- Bold API Documentation
- GitHub Repository
- npm Package
See CONTRIBUTING.md for details on how to contribute to this project.
See SECURITY.md for security policies and reporting vulnerabilities.
MIT