npm.io
0.1.0 • Published 2d ago

@eldritchlogic/heygen-sdk

Licence
MIT
Version
0.1.0
Deps
0
Size
1.1 MB
Vulns
0
Weekly
0

@eldritchlogic/heygen-sdk

Fully-typed Node.js/TypeScript SDK for the HeyGen APIevery documented endpoint is covered: videos, Video Agent, avatars, realtime avatars, voices & TTS, video translation, lipsync, AI clipping, HyperFrames, assets, webhooks, brand, workflows, background removal, plus the complete legacy (pre-v3) API surface.

  • 100% endpoint coverage, mechanically verified. A test suite walks HeyGen's published OpenAPI specs (vendored in specs/) and fails if any operation lacks an SDK method or hits the wrong method/path.
  • Fully typed. Request and response types are generated from HeyGen's official OpenAPI spec — every schema is exported by name.
  • Zero runtime dependencies. Built on the global fetch (Node.js ≥ 18.17, Bun, Deno, edge runtimes, modern browsers).
  • Batteries included. Cursor auto-pagination, job polling helpers, automatic retries with Retry-After support, idempotency keys, SSE streaming, and webhook signature verification.

Install

npm install @eldritchlogic/heygen-sdk

Quick start

import { HeyGen } from "@eldritchlogic/heygen-sdk";

const heygen = new HeyGen({ apiKey: process.env.HEYGEN_API_KEY });

// Create a video and wait for it to render.
const { video_id } = await heygen.videos.create({
  type: "avatar",
  avatar_id: "your_avatar_look_id",
  script: "Welcome to our product tour!",
  aspect_ratio: "auto",
  resolution: "1080p",
});
const video = await heygen.videos.waitForCompletion(video_id);
console.log(video.video_url);

Or let the Video Agent do everything from a prompt:

const session = await heygen.videoAgents.create({
  prompt: "A 30-second product intro for an AI note-taking app, upbeat tone",
});
const done = await heygen.videoAgents.waitForCompletion(session.session_id);

Authentication & configuration

const heygen = new HeyGen({
  apiKey: "...",            // default: process.env.HEYGEN_API_KEY (sent as x-api-key)
  accessToken: "...",       // alternative: OAuth2 bearer token
  baseUrl: "https://api.heygen.com",  // override for testing/proxies
  timeoutMs: 60_000,        // per-request timeout
  maxRetries: 2,            // automatic retries on 429/5xx/network errors
  defaultHeaders: {},       // merged into every request
  fetch: customFetch,       // bring your own fetch
});

Every method also accepts per-request options as its last argument:

await heygen.videos.create(body, {
  idempotencyKey: crypto.randomUUID(), // safe retries for mutations
  timeoutMs: 120_000,
  signal: abortController.signal,
  headers: { "x-trace-id": "..." },
});

Retries: GET/PUT/DELETE requests are retried automatically on 429, 5xx, and network errors (respecting Retry-After). POST/PATCH requests are only retried when you pass an idempotencyKey, which HeyGen deduplicates server-side for 24 h.

Pagination

List endpoints return a Page<T> with data, hasMore, and nextToken. Iterate the page to walk all pages lazily, or use nextPage() / toArray(limit):

for await (const voice of await heygen.voices.list({ language: "English" })) {
  console.log(voice.name);
}

const firstHundred = await (await heygen.videos.list()).toArray(100);

Polling helpers

Async jobs expose waitForCompletion (and friends) that poll until a terminal status:

await heygen.videos.waitForCompletion(videoId, { intervalMs: 5000, timeoutMs: 15 * 60_000 });
await heygen.videoTranslations.waitForCompletion(translationId);
await heygen.lipsyncs.waitForCompletion(lipsyncId);
await heygen.aiClipping.waitForCompletion(jobId);
await heygen.hyperframes.renders.waitForCompletion(renderId);
await heygen.backgroundRemovals.waitForCompletion(jobId);
await heygen.voices.waitForClone(voiceId);
await heygen.avatars.waitForTraining(groupId);
await heygen.videoTranslations.proofreads.waitForCompletion(proofreadId);

Prefer webhooks over polling in production — see below.

Webhooks

import { verifyWebhookSignature, WebhookVerificationError } from "@eldritchlogic/heygen-sdk";

// Register an endpoint (store the returned secret — it's only shown once):
const endpoint = await heygen.webhooks.endpoints.create({
  url: "https://yourapp.com/webhooks/heygen",
  events: ["avatar_video.success", "avatar_video.fail"],
});

// In your webhook handler (keep the raw body — parsed JSON won't verify):
app.post("/webhooks/heygen", express.raw({ type: "application/json" }), async (req, res) => {
  try {
    const event = await verifyWebhookSignature({
      payload: req.body,                          // raw Buffer/string
      signature: req.header("Heygen-Signature")!, // hex HMAC-SHA256
      timestamp: req.header("Heygen-Timestamp"),  // replay protection (±300 s)
      secret: process.env.HEYGEN_WEBHOOK_SECRET!,
    });
    // handle event ...
    res.sendStatus(200);
  } catch (err) {
    if (err instanceof WebhookVerificationError) return res.sendStatus(401);
    throw err;
  }
});

Error handling

All API failures throw typed subclasses of APIError with status, code, param, docUrl, requestId, headers, and the raw body:

Status Error class
400 BadRequestError
401 AuthenticationError
403 PermissionDeniedError
404 NotFoundError
409 ConflictError (e.g. idempotent retry still in flight)
422 UnprocessableEntityError
429 RateLimitError (with .retryAfter seconds)
5xx InternalServerError
network APIConnectionError / APITimeoutError
import { RateLimitError } from "@eldritchlogic/heygen-sdk";

try {
  await heygen.videos.create(body);
} catch (err) {
  if (err instanceof RateLimitError) console.log(`retry in ${err.retryAfter}s`);
  throw err;
}

Endpoint reference

Every operation in HeyGen's OpenAPI specs, and the SDK method that calls it.

Videos — heygen.videos
Method Endpoint
create(body) POST /v3/videos
get(videoId) GET /v3/videos/{video_id}
list(params?) GET /v3/videos
delete(videoId) DELETE /v3/videos/{video_id}
statuses(params?) GET /v3/videos/statuses
batches.create(body) POST /v3/videos/batches
batches.get(batchId, params?) GET /v3/videos/batches/{batch_id}
createV2(body) (deprecated) POST /v2/videos
getV2(videoId) (deprecated) GET /v2/videos/{video_id}
listV2(params?) (deprecated) GET /v2/videos
deleteV2(videoId) (deprecated) DELETE /v2/videos/{video_id}
Video Agent — heygen.videoAgents
Method Endpoint
create(body) POST /v3/video-agents
get(sessionId) GET /v3/video-agents/{session_id}
list(params?) GET /v3/video-agents
sendMessage(sessionId, body) POST /v3/video-agents/{session_id}
stop(sessionId, body?) POST /v3/video-agents/{session_id}/stop
listVideos(sessionId) GET /v3/video-agents/{session_id}/videos
getResource(sessionId, resourceId) GET /v3/video-agents/{session_id}/resources/{resource_id}
listStyles(params?) GET /v3/video-agents/styles
generateV1(body) (deprecated) POST /v1/video_agent/generate
Avatars — heygen.avatars
Method Endpoint
create(body) POST /v3/avatars
listGroups(params?) GET /v3/avatars
getGroup(groupId) GET /v3/avatars/{group_id}
deleteGroup(groupId) DELETE /v3/avatars/{group_id}
createConsent(groupId, body) POST /v3/avatars/{group_id}/consent
looks.list(params?) GET /v3/avatars/looks
looks.get(lookId) GET /v3/avatars/looks/{look_id}
looks.update(lookId, body) PATCH /v3/avatars/looks/{look_id}
looks.delete(lookId) DELETE /v3/avatars/looks/{look_id}
Avatar Realtime — heygen.avatarRealtime
Method Endpoint
createSession(body) POST /v3/avatar-realtime
getSession(streamId) GET /v3/avatar-realtime/{stream_id}
appendText(streamId, body) POST /v3/avatar-realtime/{stream_id}/text
cancel(streamId) POST /v3/avatar-realtime/{stream_id}/cancel
streamWords(streamId) — async iterator over SSE GET /v3/avatar-realtime/{stream_id}/words
Voices & audio — heygen.voices, heygen.audio
Method Endpoint
voices.list(params?) GET /v3/voices
voices.get(voiceId) GET /v3/voices/{voice_id}
voices.delete(voiceId) DELETE /v3/voices/{voice_id}
voices.clone(body) POST /v3/voices/clone
voices.design(body) POST /v3/voices
voices.speech(body) — TTS POST /v3/voices/speech
voices.textToSpeechV1(body) (deprecated) POST /v1/audio/text_to_speech
voices.listV1(params?) (deprecated) GET /v1/audio/voices
audio.searchSounds(params) GET /v3/audio/sounds
Templates — heygen.templates
Method Endpoint
list(params?) GET /v3/templates
get(templateId) GET /v3/templates/{template_id}
generate(templateId, body) POST /v3/templates/{template_id}
Video translation — heygen.videoTranslations
Method Endpoint
create(body) POST /v3/video-translations
get(id) GET /v3/video-translations/{video_translation_id}
list(params?) GET /v3/video-translations
update(id, body) PATCH /v3/video-translations/{video_translation_id}
delete(id) DELETE /v3/video-translations/{video_translation_id}
listLanguages() GET /v3/video-translations/languages
statuses(params?) GET /v3/video-translations/statuses
proofreads.create(body) POST /v3/video-translations/proofreads
proofreads.get(id) GET /v3/video-translations/proofreads/{proofread_id}
proofreads.downloadSrt(id) GET /v3/video-translations/proofreads/{proofread_id}/srt
proofreads.uploadSrt(id, body) PUT /v3/video-translations/proofreads/{proofread_id}/srt
proofreads.generateVideo(id, body?) POST /v3/video-translations/proofreads/{proofread_id}/generate
batches.create(body) POST /v3/video-translations/batches
batches.get(batchId, params?) GET /v3/video-translations/batches/{batch_id}
createV2(body) (deprecated) POST /v2/video_translate
listTargetLanguagesV2() (deprecated) GET /v2/video_translate/target_languages
getCaptionV2(params) (deprecated) GET /v2/video_translate/caption
Lipsync — heygen.lipsyncs
Method Endpoint
create(body) POST /v3/lipsyncs
get(lipsyncId) GET /v3/lipsyncs/{lipsync_id}
list(params?) GET /v3/lipsyncs
update(lipsyncId, body) PATCH /v3/lipsyncs/{lipsync_id}
delete(lipsyncId) DELETE /v3/lipsyncs/{lipsync_id}
statuses(params?) GET /v3/lipsyncs/statuses
batches.create(body) POST /v3/lipsyncs/batches
batches.get(batchId, params?) GET /v3/lipsyncs/batches/{batch_id}
HyperFrames — heygen.hyperframes
Method Endpoint
renders.create(body) POST /v3/hyperframes/renders
renders.get(renderId) GET /v3/hyperframes/renders/{render_id}
renders.list(params?) GET /v3/hyperframes/renders
renders.delete(renderId) DELETE /v3/hyperframes/renders/{render_id}
AI Clipping — heygen.aiClipping
Method Endpoint
create(body) POST /v3/ai-clipping
get(jobId) GET /v3/ai-clipping/{job_id}
list(params?) GET /v3/ai-clipping
delete(jobId) DELETE /v3/ai-clipping/{job_id}
Assets — heygen.assets
Method Endpoint
upload(file, params?) — multipart POST /v3/assets
get(assetId) GET /v3/assets/{asset_id}
list(params) GET /v3/assets
delete(assetId) DELETE /v3/assets/{asset_id}
search(params) GET /v3/assets/search
createDirectUpload(body) POST /v3/assets/direct-uploads
completeUpload(assetId, body?) POST /v3/assets/{asset_id}/complete
statuses(params?) GET /v3/assets/statuses
batches.createDirectUploads(body) POST /v3/assets/direct-uploads/batches
batches.complete(body) POST /v3/assets/complete/batches
batches.get(batchId, params?) GET /v3/assets/batches/{batch_id}
Webhooks — heygen.webhooks
Method Endpoint
endpoints.create(body) POST /v3/webhooks/endpoints
endpoints.list(params?) GET /v3/webhooks/endpoints
endpoints.update(endpointId, body) PATCH /v3/webhooks/endpoints/{endpoint_id}
endpoints.delete(endpointId) DELETE /v3/webhooks/endpoints/{endpoint_id}
endpoints.rotateSecret(endpointId) POST /v3/webhooks/endpoints/{endpoint_id}/rotate-secret
listEventTypes() GET /v3/webhooks/event-types
listEvents(params?) GET /v3/webhooks/events
verify(params) — HMAC verification helper
Brand, users, workflows, background removal
Method Endpoint
brand.listKits(params?) GET /v3/brand-kits
brand.listGlossaries(params?) GET /v3/brand-glossaries
users.me() GET /v3/users/me
users.meV1() (deprecated) GET /v1/user/me
workflows.list() GET /v1/workflows
workflows.createExecution(body) POST /v1/workflows/executions
workflows.createGraphExecution(body) POST /v1/workflows/graph-executions
workflows.getExecution(executionId) GET /v1/workflows/executions/{execution_id}
backgroundRemovals.create(body) POST /v3/background-removals
backgroundRemovals.get(jobId) GET /v3/background-removals/{job_id}
backgroundRemovals.list(params?) GET /v3/background-removals
backgroundRemovals.delete(jobId) DELETE /v3/background-removals/{job_id}
Legacy API — heygen.legacy

The complete pre-v3 surface (supported by HeyGen until October 31, 2026). Prefer the v3 resources above for new work; these methods return loosely-typed objects where HeyGen does not document response schemas.

Namespace Endpoints
legacy.streaming newSession, startSession, listSessions, sendTask, interruptTask, stopSession, createSessionToken, listAvatars/v1/streaming.*
legacy.photoAvatars generatePhotos, generateLooks, getGeneration, createGroup, addLooksToGroup, train, getTrainingStatus, addMotion, addSoundEffect, upscale, get, listGroups, listGroupAvatars/v2/photo_avatar/*, /v2/avatar_group*
legacy.videoAvatars create, getStatus, delete/v2/video_avatar
legacy.videos generate, getStatus, list, delete, createWebm/v2/video/generate, /v1/video_status.get, /v1/video.list, /v1/video.delete, /v1/video.webm
legacy.templates list, get, generate, getVariableSchema/v2/templates, /v2/template/{id}, /v2/template/{id}/generate, /v3/template/{id}
legacy.webhooks listEndpoints, addEndpoint, updateEndpoint, deleteEndpoint, listAvailableEvents/v1/webhook/*
legacy.folders create, list, update, trash, restore/v1/folders*
legacy.brandVoices list, update/v1/brand_voice/*
legacy.avatars listGET /v2/avatars
legacy.voices listGET /v2/voices
legacy.user getRemainingQuotaGET /v2/user/remaining_quota
legacy.videoTranslate getStatusGET /v2/video_translate/{id}
legacy.assets upload(file, contentType)POST upload.heygen.com/v1/asset

Types

Every schema in HeyGen's OpenAPI spec is exported by name:

import type { CreateVideoV3RequestBody, VideoDetail, AvatarLookItem } from "@eldritchlogic/heygen-sdk";

Regenerating types

The specs are vendored in specs/. To refresh from HeyGen's published spec:

curl -s https://developers.heygen.com/openapi/external-api.json -o specs/external-api.json
npm run gen:types   # regenerates src/generated/*, then `npm test` verifies coverage

If HeyGen adds an endpoint, the coverage test fails until the SDK implements it.

License

MIT

Keywords