> ## 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.

# Creating videos

> Which endpoint makes each format, what create returns, and how a project becomes a video

## Overview

Each video format has its own endpoint under `POST /v1/project/create/{format}`. There is no generic `POST /v1/project/create`: pick the endpoint for the format you want.

Every create endpoint works the same way:

<Steps>
  <Step title="Create the project">
    `POST /v1/project/create/{format}` validates the request, checks your team can pay for it, and answers right away with a `projectId`. Generation runs in the background.

    ```json theme={null}
    {
      "success": true,
      "data": { "projectId": "cm4x9k2p10001ab12cd34ef56", "status": "processing" },
      "message": "Script to Video successfully created"
    }
    ```

    `data.status` is the project status (`draft`, `processing`, `completed` or `failed`).
  </Step>

  <Step title="Wait for the video">
    Every format renders automatically when generation finishes: the project's `video` appears and the `webhook` is called. [Render Project](/api-reference/video/render) is only needed to render again after editing.

    Either poll [Get Project](/api-reference/project/details) with the `projectId`, or pass a `webhook` URL on create and Hooked calls it when the video is ready or the project fails (see [Webhooks](/guides/webhooks)).
  </Step>

  <Step title="Download it">
    When the project's `video.status` is `COMPLETED`, download `video.url`. The same video is available from [Get Video Details](/api-reference/video/details) and [List Videos](/api-reference/video/list).
  </Step>
</Steps>

<Info>
  The create response has no `videoId`. The video exists only once the project's render starts; it then appears under `video` in [Get Project](/api-reference/project/details).
</Info>

## Create endpoints

### Avatars

| Endpoint | Format |
| - | - |
| [`/v1/project/create/talking-avatar`](/api-reference/video/talking-avatar) | An AI avatar speaks your script on camera. |
| [`/v1/project/create/ugc-studio/product-in-hand`](/api-reference/ugc-studio/product-in-hand) | UGC Studio: the avatar shows and uses your product, with optional B-roll. |
| [`/v1/project/create/ugc-studio/app-demo`](/api-reference/ugc-studio/app-demo) | UGC Studio: the avatar pitches your app or website over a screen recording. |
| [`/v1/project/create/ugc-studio/fashion`](/api-reference/ugc-studio/fashion) | UGC Studio: the avatar wears your outfits, one look per clip. |
| [`/v1/project/create/ugc-studio/before-after`](/api-reference/ugc-studio/before-after) | UGC Studio: a before / after transformation ad. |
| [`/v1/project/create/ugc-studio/voiceover`](/api-reference/ugc-studio/voiceover) | UGC Studio: a narrator reads your script over silent scenes of the avatar. |
| [`/v1/project/create/podcast-interview`](/api-reference/video/podcast-interview) | Podcast / Dualcast: two people talking, host and guest cut back and forth (podcast) or both in one shot (dualcast). |
| [`/v1/project/create/hook-demo`](/api-reference/video/hook-demo) | A short avatar reaction hook followed by your demo footage. |
| [`/v1/project/create/scenes`](/api-reference/video/scenes) | A sequence of scenes: avatar, video clip, picture-in-picture, start/end frame. |

### Narrated videos

| Endpoint | Format |
| - | - |
| [`/v1/project/create/script-to-video`](/api-reference/video/script-to-video) | Your script, narrated, with AI images, AI video, your media, gameplay or motion graphics. |
| [`/v1/project/create/prompt-to-video`](/api-reference/video/prompt-to-video) | Same, but the script is written from your prompt. |
| [`/v1/project/create/reddit-story`](/api-reference/video/reddit-story) | A Reddit-style story written from your prompt and narrated over a background. |
| [`/v1/project/create/quiz-video`](/api-reference/video/quiz-video) | A narrated quiz from your questions. |
| [`/v1/project/create/tiktok-slideshow`](/api-reference/video/tiktok-slideshow) | A slideshow of your images or clips with text, optionally narrated by an avatar. |
| [`/v1/project/create/scrolling-video`](/api-reference/video/scrolling-video) | A recording of a website scrolling. |

### From a source

| Endpoint | Format |
| - | - |
| [`/v1/project/create/article-to-video`](/api-reference/video/article-to-video) | A web article, summarised (or kept as is) and narrated over AI images, AI video, your media or gameplay. |
| [`/v1/project/create/pdf-to-video`](/api-reference/video/pdf-to-video) | The same from a PDF, sent as a public link (`pdfUrl`) or as the file (`pdfBase64`). |
| [`/v1/project/create/pdf-to-brainrot`](/api-reference/video/pdf-to-brainrot) | A PDF narrated over a gameplay clip. |
| [`/v1/project/create/music-to-video`](/api-reference/video/music-to-video) | A music video for your song (`audioUrl` or `musicId`): AI images, AI video, your media or gameplay following the lyrics, cut to the beat. |
| [`/v1/project/create/clone-video`](/api-reference/video/clone-video) | A TikTok, Instagram or YouTube video remade with your voice and new AI visuals. |

Article, PDF and Cinematic requests answer in a few seconds with the project in `processing`: reading the source (or writing the storyboard) happens after the answer. If that step fails, the project becomes `failed` with the reason in `message`, the credits come back and your webhook gets the failure. Send an [`Idempotency-Key`](/guides/idempotency) so a retry after a network error cannot create the project twice.

### Editing your own video

| Endpoint | Format |
| - | - |
| [`/v1/project/create/add-captions`](/api-reference/video/add-captions) | Transcribe a video and burn captions in. |
| [`/v1/project/create/extend-video`](/api-reference/video/extend-video) | Continue a video with AI-generated footage. |
| [`/v1/project/create/remove-background`](/api-reference/video/remove-background) | Cut the person out of a video. |

To render an existing project again (for example after editing it in the dashboard), use [Render Project](/api-reference/video/render).

## Find the ids you need

Create requests take ids, never names. Every one of them can be read from the API, so nothing has to be copied from the dashboard:

| What | Endpoint | Used as |
| - | - | - |
| Avatars (library and your own) | [List Avatars](/api-reference/avatar/list) | `avatarId` |
| Voices (library and your own) | [List Voices](/api-reference/voice/list) | `voiceId` |
| Music | [List Music](/api-reference/music/list) | `musicId` |
| Images and videos of your library | [List Media](/api-reference/media/list) | `media`, `mediaId`, image keys |
| Cinematic characters | [List Characters](/api-reference/character/list) | `characterIds` (Cinematic) |
| Fixed lists: caption styles, visual styles, motion styles, games, hook reactions, AI models, Cinematic options, camera moves, gestures, voice cards, accents | [Get Catalog](/api-reference/catalog/get) | `caption.preset`, `presetSettings.preset`, `aiModel`, `gameplaySettings`, `videoStyle`… |

The catalogs are read from the same lists the create endpoints validate against: an id a catalog returns is accepted, and anything else answers `400` with the allowed values. Your team's own custom styles and reactions are included.

```bash theme={null}
curl "https://api.hooked.so/v1/catalog/caption-presets" -H "x-api-key: $HOOKED_API_KEY"
curl "https://api.hooked.so/v1/catalog/visual-styles" -H "x-api-key: $HOOKED_API_KEY"
```

Before a batch, [Get Account](/api-reference/account/get) tells you the credit balance, whether your team is on Hooked keys or its own (and, with its own, which provider keys are set and working), and how much storage is left.

## Fields every create endpoint accepts

| Field | Type | What it does |
| - | - | - |
| `name` | string | Project name, up to 100 characters. When omitted, Hooked names the project from its content (a quiz takes its `title`). |
| `webhook` | string | HTTPS URL on a public host, up to 500 characters, called when the video is ready or the project fails. A URL that is not `https://` or names a private host (`localhost`, a private IP, `*.local`...) is refused with `400 webhook: Must be a valid HTTPS URL`. See [Webhooks](/guides/webhooks). |
| `metadata` | object | Any JSON object up to 5 KB once serialized. Returned in [Get Project](/api-reference/project/details) and in the webhook payload, so you can match the video to your own records. The key `automationId` is reserved for Hooked's dashboard automations: it is removed from the object you send, and the rest is stored as-is. |

Each endpoint documents only the fields that change its video, and says when a field only applies under a condition (a format, a media type, a toggle). A field an endpoint does not list has no effect there.

Media you pass (`media`, `mediaId`, image keys) must be files in your team's library. An ID that does not exist or belongs to another team answers `400`, and so does one that is not `COMPLETED` yet.

### Get media into your library

* **The file is online**: [Import Media from URL](/api-reference/media/import) with its public `https` URL. It answers `202` with a `mediaId`; poll [Get Media](/api-reference/media/details) until `status` is `COMPLETED`.
* **The file is on your machine**: [Upload Media](/api-reference/media/upload) gives you a presigned URL; PUT the file there, then call [Complete Upload](/api-reference/media/complete).
* **It is already in your library** (uploaded or generated in the dashboard): find its id with [List Media](/api-reference/media/list).

```bash theme={null}
curl -X POST "https://api.hooked.so/v1/media/import" \
  -H "x-api-key: $HOOKED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://cdn.example.com/products/serum.png" }'
```

Images and videos up to 100 MB (JPEG, PNG, WEBP, GIF, MP4, MOV, WEBM); they count against your plan's storage.

## Credits and your own keys

Teams run in one of two modes:

* **Hooked keys (managed).** Generation runs on Hooked's provider accounts and is charged in credits, either up front when the project is created or step by step while it generates. If a project fails, the credits charged for it are refunded automatically. A managed team needs an active subscription; without one, create answers `402 subscription_required`. Without enough credits it answers `402 INSUFFICIENT_CREDITS`.
* **Your own keys (BYOK).** Your team adds its own provider keys in **Settings → AI keys** in the dashboard. Generation is billed by those providers to you, and Hooked charges **no credits** for it. Each format needs specific keys; if one is missing or invalid, create answers `402 missing_credentials` with the list in `missingProviders`, before anything is created or charged.

| Key | Needed for |
| - | - |
| OpenRouter | Every format (scripts, project names, images, video models, music, transcription for captions). |
| ElevenLabs | Formats that synthesize a voice: script to video, prompt to video, reddit story, quiz, scenes, and the voiceover format of UGC Studio. Not needed when the cast speaks with the AI video model's own voice. |
| Gemini | Avatar clips: talking avatar, UGC Studio (except the voiceover format), podcast / dualcast, avatar and picture-in-picture scenes, slideshows with an avatar, and any AI video made with the Omni Flash model. |
| Bria | Background removal: remove background, picture-in-picture scenes, UGC formats that cut the avatar out, slideshows with an avatar. |

Each endpoint page lists the exact keys for that format. Rendering a project ([Render Project](/api-reference/video/render)) never costs credits.

## Errors

All create endpoints share these responses:

| Status | Body | Cause |
| - | - | - |
| 400 | `{ "success": false, "message": "<field>: <reason>" }` | Validation failed, e.g. `script: Script is required`, `media: Media "..." not found`, `webhook: Must be a valid HTTPS URL`. A few checks answer without a field prefix, such as `Invalid JSON body` or `Media type is required`. |
| 400 | `{ "success": false, "message": "Subscription not found" }` | The team has no billing record at all (a team on its own keys that never had a plan). |
| 401 | `{ "code": "not_authenticated", "errorCode": "NOT_AUTHENTICATED", ... }` | Missing or invalid `x-api-key`. |
| 402 | `{ "code": "missing_credentials", "errorCode": "MISSING_CREDENTIALS", "missingProviders": [...] }` | BYOK team without a key this format needs. |
| 402 | `{ "success": false, "code": "subscription_required", "errorCode": "SUBSCRIPTION_REQUIRED" }` | Managed team without an active subscription. |
| 402 | `{ "success": false, "errorCode": "INSUFFICIENT_CREDITS", "creditsNeeded": 120, "creditsAvailable": 40 }` | Not enough credits for this project's estimate. A team with no credits left at all is stopped before the project is priced, with `creditsNeeded: 0`. |
| 403 | `{ "code": "entitlement_required", "error": "entitlement_required", "product": "app" }` | Your team does not have the Hooked app product. |
| 500 | `{ "success": false, "message": "Internal server error" }` | Unexpected error. |

<Warning>
  If a create call times out or returns a 5xx, the project may still have been created and charged. Send an [`Idempotency-Key`](/guides/idempotency) header with every create: a retry with the same key and body returns the first answer instead of creating a second project. Without one, check [List Projects](/api-reference/project/list) (`source=api`, newest first; a project is listed as soon as it is created) or wait for your webhook before retrying. Retrying `GET` requests is always safe.
</Warning>

## Example

```bash theme={null}
curl -X POST "https://api.hooked.so/v1/project/create/talking-avatar" \
  -H "x-api-key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "script": "Three things I wish I knew before I started running.",
    "avatarId": "cm4x8a1b20002ab12gh34ij56",
    "webhook": "https://example.com/hooked/webhook?token=YOUR_SECRET",
    "metadata": { "orderId": "A-1042" }
  }'

# Then, with the projectId from the response:
curl "https://api.hooked.so/v1/project/cm4x9k2p10001ab12cd34ef56" \
  -H "x-api-key: your_api_key_here"
```

## Related

<CardGroup cols={2}>
  <Card title="Get Project" icon="folder-open" href="/api-reference/project/details">
    Follow a project to its video
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Get notified when a video is ready
  </Card>

  <Card title="Complete Workflow" icon="diagram-project" href="/guides/complete-workflow">
    Resources, create, wait, download
  </Card>

  <Card title="Quickstart" icon="rocket" href="/quickstart">
    End-to-end tutorial
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.