npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bevyl-ai/bevyl-sdk

v0.9.1

Published

Official TypeScript SDK for the Bevyl API: signed requests, uploads, project generation and history, exports, and webhook verification.

Readme

Bevyl TypeScript SDK

@bevyl-ai/bevyl-sdk is the typed client for the Bevyl partner API. It supports workspace setup, brand updates, video uploads, project generation and history, exports, and webhook verification.

Requires Node.js 18 or newer and ES modules.

npm install @bevyl-ai/bevyl-sdk

Create a client

import { BevylClient } from '@bevyl-ai/bevyl-sdk';

const bevyl = new BevylClient({
  apiKey: process.env.BEVYL_API_KEY!,
  workspaceId: 'workspace-id',
});

await bevyl.createWorkspace({ name: 'Tasty Burgers' });
await bevyl.updateBrand({
  name: 'Tasty Burgers',
  summary: 'A neighborhood burger restaurant.',
});

const brand = await bevyl.getBrand();
console.log(brand.summary);

createWorkspace is idempotent. The client sends its workspaceId and API key with every request.

Upload and generate

uploadVideo prepares the upload, sends the file to the signed URL, and completes the upload:

import { createReadStream } from 'node:fs';
import { stat } from 'node:fs/promises';
import { Readable } from 'node:stream';

const path = 'kitchen-broll.mp4';
const file = createReadStream(path);

try {
  const upload = await bevyl.uploadVideo({
    filename: path,
    fileSize: (await stat(path)).size,
    durationSeconds: 30,
    contentType: 'video/mp4',
    body: Readable.toWeb(file),
  });
  console.log(upload.video.id);
} finally {
  file.destroy();
}

Wait for the video to reach processed before creating a project. Wait for the project to reach completed before starting an export.

const videoStatus = await bevyl.getVideoStatus({ videoId });
if (videoStatus.video.status !== 'processed') {
  throw new Error(videoStatus.video.statusMessage);
}

const voices = await bevyl.listVoices();

const project = await bevyl.createProject({
  format: 'voiceover',
  sourceVideoIds: [videoStatus.video.id],
  voiceoverProfileId: voices.defaultVoiceoverProfileId,
  videoIdea: 'A 15-second vertical ad for the truffle burger.',
  aspectRatio: '9:16',
});

const projectStatus = await bevyl.getProjectStatus({
  projectId: project.projectId,
  runId: project.generation.runId,
});
if (projectStatus.generation.status !== 'completed') {
  throw new Error(
    projectStatus.generation.statusMessage ??
      `Generation ended with ${projectStatus.generation.status}`,
  );
}

const started = await bevyl.startExport({ projectId: project.projectId });
await bevyl.getExportStatus({ exportId: started.exportId });

For background music with no generated narration, use the voiceover format with an explicit null voice and a catalog track ID:

const { tracks } = await bevyl.listBackgroundMusic();

const musicOnlyProject = await bevyl.createProject({
  format: 'voiceover',
  sourceVideoIds: [videoStatus.video.id],
  voiceoverProfileId: null,
  backgroundMusicTrackId: tracks[0]!.id,
  videoIdea: 'Caption the lunch rush with upbeat background music.',
  aspectRatio: '9:16',
});

For a trending-sounds project, select a published trend first:

const { trends } = await bevyl.listTrends();
const trend = trends[0];

if (!trend) {
  throw new Error('No published trends are available');
}

await bevyl.createProject({
  format: 'trending-sounds',
  sourceVideoIds: [videoStatus.video.id],
  trendId: trend.id,
  videoIdea: 'Cut the lunch rush to the beat.',
});

aspectRatio ('9:16', '4:5', '1:1', or '16:9') is optional; when omitted, Bevyl infers it from the source videos and defaults to vertical.

Status responses in a working state include a suggested pollIntervalMs. Terminal failures do not emit completion webhooks, so keep polling as a fallback.

Regenerate a project

regenerateProject queues a new generation for an existing project. Omitted brief fields carry over from the most recent generation; provided fields override it, and an explicit null clears a prior userScript, userMoments, minDuration, or maxDuration. The response has the same shape as createProject.

const regen = await bevyl.regenerateProject({
  projectId: project.projectId,
  videoIdea: 'Same cut, but focus on dessert and slow the pacing.',
  maxDuration: 30,
});

await bevyl.getProjectStatus({
  projectId: project.projectId,
  runId: regen.generation.runId,
});

Regeneration returns 409 generation_in_progress while a generation is running for the project, and 404 project_not_found when the project is not in the mapped workspace. The regenerated timeline replaces the applied edit; earlier versions remain available as snapshots in the Bevyl editor.

Review project history

listProjects returns projects newest first. Each project includes its generation attempts newest first and the curated creative brief used for each attempt. Pass nextCursor back to retrieve the next page.

const page = await bevyl.listProjects({ limit: 20 });

for (const project of page.projects) {
  console.log(project.title, project.generations);
}

const nextPage = page.nextCursor
  ? await bevyl.listProjects({ limit: 20, cursor: page.nextCursor })
  : null;

Reuse uploaded source videos

listVideos returns upload sessions from the scoped partner workspace, newest first, including uploads and processing that are still in progress. Every item has an uploadId; videoId remains null until upload completion creates the source video. Use a processed item's videoId in a later createProject call without uploading the same source again.

const page = await bevyl.listVideos({ limit: 20 });

for (const video of page.videos) {
  console.log(video.title, video.status, video.durationSeconds);
}

const nextPage = page.nextCursor
  ? await bevyl.listVideos({ limit: 20, cursor: page.nextCursor })
  : null;

Webhooks

Verify the signature against the raw request body, not parsed and re-serialized JSON:

import { SIGNATURE_HEADER, parseWebhookEvent } from '@bevyl-ai/bevyl-sdk';

const event = parseWebhookEvent({
  rawBody,
  signature: request.headers.get(SIGNATURE_HEADER),
  secret: process.env.BEVYL_WEBHOOK_SECRET!,
});

Supported events are broll.processed, project.completed, and export.ready. Use event.eventId as an idempotency key.

Webhook signatures use x-webhook-signature: sha256=<hex HMAC-SHA256 of the raw body>. The lower-level signBody and verifySignature functions are also exported.

Schemas and errors

Request and response schemas are available from the separate schema export:

import { CreateProjectRequestSchema } from '@bevyl-ai/bevyl-sdk/schemas';

const request = CreateProjectRequestSchema.parse(input);

Non-2xx API responses throw BevylApiError. It exposes the HTTP status, the raw response body, and a machine-readable code when the API provides one.