> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hooked.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Webhook

> Receive real-time notifications when your video is ready

## Overview

Instead of polling the [Get Video Details](/api-reference/video/details) endpoint, configure a webhook URL when creating a video to receive a notification when processing completes.

<Info>
  **Recommended**: Always use webhooks for production applications. Polling is inefficient and can hit rate limits.
</Info>

## Setting Up Webhooks

Include a `webhook` URL in your video creation request:

```bash theme={null}
curl -X POST "https://api.hooked.so/v1/project/create" \
  -H "x-api-key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "class",
    "script": "Your video script here...",
    "avatarId": "avatar_sarah_01",
    "voiceId": "tzX5paJ07p5hyWFcU3uG",
    "webhook": "https://your-domain.com/api/webhook"
  }'
```

## Webhook Payload

When your video completes, Hooked sends a `POST` request to your webhook URL:

### Success

```json theme={null}
{
  "event": "project.completed",
  "projectId": "proj_abc123",
  "status": "completed",
  "video": {
    "id": "video_xyz789",
    "url": "https://cdn.hooked.so/videos/xyz789.mp4",
    "duration": 45,
    "thumbnail": "https://cdn.hooked.so/thumbnails/xyz789.jpg",
    "aspectRatio": "9:16",
    "resolution": "1080x1920"
  },
  "createdAt": "2024-01-15T10:30:00Z",
  "completedAt": "2024-01-15T10:33:00Z"
}
```

### Failure

```json theme={null}
{
  "event": "project.failed",
  "projectId": "proj_abc123",
  "status": "failed",
  "message": "Script too short to generate video",
  "createdAt": "2024-01-15T10:30:00Z"
}
```

## Payload Fields

| Field             | Type   | Description                             |
| ----------------- | ------ | --------------------------------------- |
| `event`           | string | `project.completed` or `project.failed` |
| `projectId`       | string | The project ID                          |
| `status`          | string | `completed` or `failed`                 |
| `video`           | object | Video details (only when completed)     |
| `video.url`       | string | Download URL (expires in 7 days)        |
| `video.duration`  | number | Duration in seconds                     |
| `video.thumbnail` | string | Thumbnail image URL                     |
| `message`         | string | Error message (only when failed)        |

## Implementation Example

```javascript theme={null}
app.post('/api/webhook', express.json(), (req, res) => {
  const { event, projectId, status, video } = req.body;

  if (event === 'project.completed' && status === 'completed') {
    console.log('Video ready:', video.url);
    // Save URL, notify user, etc.
  } else if (event === 'project.failed') {
    console.error('Video failed:', req.body.message);
  }

  res.status(200).send('OK');
});
```

## Retry Policy

If your endpoint doesn't return a `200` status, Hooked retries:

| Attempt   | Delay      |
| --------- | ---------- |
| 1st retry | 1 minute   |
| 2nd retry | 5 minutes  |
| 3rd retry | 15 minutes |

After 3 failed attempts, no more retries are made.

<Warning>
  Always return `200 OK` immediately, even if your processing fails. This prevents unnecessary retries.
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="Webhooks Guide" icon="book" href="/guides/webhooks">
    In-depth webhook guide with security and testing
  </Card>

  <Card title="Get Video Details" icon="info" href="/api-reference/video/details">
    Poll video status as an alternative
  </Card>

  <Card title="List Videos" icon="list" href="/api-reference/video/list">
    View all your videos
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/guides/error-handling">
    Handle errors gracefully
  </Card>
</CardGroup>
