# HeyGen Webhooks

## When to Use This Skill

* How do I receive HeyGen webhooks?
* How do I verify the HeyGen webhook `signature` header?
* How do I know when a HeyGen avatar video has finished rendering (`avatar_video.success`)?
* How do I handle `video_translate.success` or `video_translate.fail` events?
* Why is my HeyGen webhook signature verification failing?

## Verification (core)

HeyGen sends the signature in a header literally named `signature`: the **hex-encoded HMAC-SHA256 of the raw request body**, with no timestamp and no prefix. The key is the endpoint `secret` returned when you create the endpoint (it looks like `whsec_k7x9m2...`), used **whole, as a UTF-8 string**.

> **This is NOT Standard Webhooks.** Do not use the `standardwebhooks` or `svix` libraries, and do not strip the `whsec_` prefix or base64-decode the secret. There are no `webhook-id` / `webhook-timestamp` / `webhook-signature` headers.

```javascript
const crypto = require('crypto');

function verifyHeyGenSignature(rawBody, signature, secret) {
  if (!signature || !secret) return false;
  const expected = crypto
    .createHmac('sha256', secret)      // secret as-is, including "whsec_"
    .update(rawBody)                   // raw Buffer, not parsed JSON
    .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(signature.trim().toLowerCase());
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

```

HeyGen has no webhook-verification SDK, so all three examples verify manually. Read the raw body first (in Express, `express.raw({ type: '*/*' })` so any content type verifies).

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/heygen-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/heygen-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/heygen-webhooks/examples/fastapi/).

## Common Event Types

The event type is in the payload's `event_type` field and the data is in `event_data`.

| Event | Triggered When |
| --- | --- |
| `avatar_video.success` | An avatar video finished rendering (`event_data.video_id`, `url`, `callback_id`, ...) |
| `avatar_video.fail` | Avatar video generation failed |
| `video_translate.success` | A video translation completed |
| `video_translate.fail` | A video translation failed |
| `video_agent.success` / `video_agent.fail` | A Video Agent session completed / failed |

> **For the full event list** (GIF, caption, avatar training, proofread, live avatar, ...), see [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/heygen-webhooks/references/overview.md) and [HeyGen's webhook events docs](https://developers.heygen.com/docs/webhook-events).

## Environment Variables

```bash
# Signing secret returned once by POST /v3/webhooks/endpoints (or rotate-secret)
HEYGEN_WEBHOOK_SECRET=whsec_your_endpoint_secret

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 heygen --path /webhooks/heygen

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/heygen-webhooks/references/overview.md) - HeyGen webhook concepts, event list, payload shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/heygen-webhooks/references/setup.md) - Register endpoints via the v3 API, get and rotate the secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/heygen-webhooks/references/verification.md) - Signature verification details and gotchas