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

# Create video

> Queue 1–50 video generation jobs in a single request

## Overview

`POST /v1/videos` is the programmatic entry point for the Knowlify Animation Engine. A single call queues 1–50 video generation jobs and returns immediately with a job ID for each one. Videos render asynchronously in the background — use [`GET /v1/videos/{uuid}`](/api-reference/poll-video) to poll, or subscribe to Supabase realtime for live updates.

<Info>
  This endpoint is asynchronous. The response confirms each job was accepted (`queued` or `parked`); rendering itself takes minutes and is delivered out-of-band.
</Info>

## Endpoint

```
POST {API_BASE}/v1/videos
```

<Note>
  Your `API_BASE` is shown in the Developer tab of your dashboard. The default production base is `https://api.knowlify.com`.
</Note>

## Authentication

Send your key in the `X-API-Key` header. Personal keys are scoped to your user; organization keys are scoped to a single org.

```bash theme={null}
X-API-Key: kn_<64 hex chars>
```

See [Authentication](/api-reference/authentication) for full details on key issuance, rotation, and JWT alternatives.

## Request body

<ParamField body="videos" type="VideoCreateItem[]" required>
  Array of 1–50 video specs to queue. Items beyond your concurrency limit are accepted but parked in a waiting list — see [Rate Limits](/api-reference/rate-limits#concurrency).
</ParamField>

<ParamField body="email" type="string">
  Advisory only — when authenticating with an API key, identity comes from the key itself. Maximum 320 characters; must match `local@domain.tld`.
</ParamField>

<ParamField body="org_id" type="string">
  Optional organization scope. For API keys this must match the org the key belongs to (or be omitted). For JWT callers, the server verifies active membership.
</ParamField>

### `VideoCreateItem`

<Expandable title="VideoCreateItem fields">
  <ParamField body="task" type="string" required>
    The instruction describing what the video should explain or show. 1–5000 characters.
  </ParamField>

  <ParamField body="video_duration_seconds" type="integer" required>
    Target video length in seconds. Range: `30`–`300`.
  </ParamField>

  <ParamField body="aspect_ratio" type="string" default="16:9">
    Output aspect ratio. One of `"16:9"` (landscape) or `"9:16"` (portrait).
  </ParamField>

  <ParamField body="video_type" type="string">
    Style preset. One of: `"instructional"`, `"explainer"`, `"corporate"`, `"marketing"`, `"narrative"`, `"social"`.
  </ParamField>

  <ParamField body="global_style_prompt" type="string">
    Free-form direction applied to every scene (e.g., "minimal, brand-blue accents, sans-serif"). Maximum 5000 characters.
  </ParamField>

  <ParamField body="color_palette" type="object">
    Optional brand palette. Allowed keys: `primary`, `secondary`, `tertiary`, `accent`. Each value must be a hex color (`#fff` or `#ffffff`).

    ```json theme={null}
    { "primary": "#0066cc", "accent": "#ff6600" }
    ```
  </ParamField>

  <ParamField body="voice_id" type="string">
    Voice provider ID for narration. Maximum 200 characters.
  </ParamField>

  <ParamField body="voice_name" type="string">
    Display name for the chosen voice. Maximum 200 characters.
  </ParamField>

  <ParamField body="voice_description" type="string">
    Free-form description of the desired narration style. Maximum 2000 characters.
  </ParamField>

  <ParamField body="reference_image_urls" type="(string | object)[]">
    Up to 20 reference images. Each entry can be an `http(s)` URL string or an object `{ "url": "..." }`.
  </ParamField>

  <ParamField body="reference_video_url" type="string">
    A single user-uploaded reference video (`http(s)` URL, ≤60s).
  </ParamField>

  <ParamField body="pdf_url" type="string">
    `http(s)` URL of a source PDF to draw content from.
  </ParamField>

  <ParamField body="pdf_file_name" type="string">
    Display name for the PDF (used for chat context). Maximum 500 characters.
  </ParamField>

  <ParamField body="pdf_selected_images" type="object[]">
    Up to 50 image selections extracted from the PDF.
  </ParamField>

  <ParamField body="is_rxv" type="boolean" default="true">
    Internal mode flag. Leave as default unless instructed otherwise.
  </ParamField>

  <ParamField body="auto_approve_voiceover" type="boolean" default="true">
    Skips the manual voiceover-approval step. Always `true` for programmatic calls.
  </ParamField>
</Expandable>

## Response

A successful call returns HTTP `200` even when individual items fail — inspect each `results[].status` to know what to retry.

<ResponseField name="status" type="string">
  Always `"ok"` on a 2xx response.
</ResponseField>

<ResponseField name="total" type="integer">
  Number of items in the request.
</ResponseField>

<ResponseField name="enqueued" type="integer">
  Items dispatched to a worker immediately.
</ResponseField>

<ResponseField name="parked" type="integer">
  Items accepted but waiting for a free concurrency slot.
</ResponseField>

<ResponseField name="errors" type="integer">
  Items that failed validation or queueing.
</ResponseField>

<ResponseField name="user_id" type="string">
  UUID of the authenticated caller.
</ResponseField>

<ResponseField name="org_id" type="string">
  Organization ID if the key/JWT is org-scoped, otherwise `null`.
</ResponseField>

<ResponseField name="results" type="object[]">
  One entry per request item, in the same order as `videos`.

  <Expandable title="result fields">
    <ResponseField name="index" type="integer">
      Position of this item in the original `videos` array.
    </ResponseField>

    <ResponseField name="uuid" type="string">
      The job ID — pass to `GET /v1/videos/{uuid}` to poll status.
    </ResponseField>

    <ResponseField name="session_id" type="string">
      Chat session ID for the job.
    </ResponseField>

    <ResponseField name="chat_url" type="string">
      Direct link to the dashboard view for this job.
    </ResponseField>

    <ResponseField name="status" type="string">
      One of `"queued"`, `"parked"`, `"already_processing"`, or `"error"`.
    </ResponseField>

    <ResponseField name="queue_position" type="integer">
      Present when `status` is `"parked"` — 1-indexed position in your waiting list.
    </ResponseField>

    <ResponseField name="code" type="string">
      Present when `status` is `"error"` (e.g., `"ITEM_FAILED"`).
    </ResponseField>

    <ResponseField name="message" type="string">
      Present when `status` is `"error"` — human-readable description.
    </ResponseField>
  </Expandable>
</ResponseField>

### Response headers

Every successful response carries the current rate-limit window state:

```
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 27
X-RateLimit-Reset: 42
```

## Code examples

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://api.knowlify.com/v1/videos" \
    -H "X-API-Key: kn_YOUR_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "videos": [
        {
          "task": "Explain how HTTPS works in 30 seconds",
          "video_duration_seconds": 60,
          "aspect_ratio": "16:9",
          "video_type": "explainer",
          "color_palette": { "primary": "#0066cc", "accent": "#ff6600" }
        },
        {
          "task": "Quarterly results recap",
          "video_duration_seconds": 45,
          "aspect_ratio": "9:16",
          "video_type": "corporate"
        }
      ]
    }'
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch("https://api.knowlify.com/v1/videos", {
    method: "POST",
    headers: {
      "X-API-Key": "kn_YOUR_KEY_HERE",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      videos: [
        {
          task: "Explain how HTTPS works in 30 seconds",
          video_duration_seconds: 60,
          aspect_ratio: "16:9",
          video_type: "explainer",
          color_palette: { primary: "#0066cc", accent: "#ff6600" },
        },
        {
          task: "Quarterly results recap",
          video_duration_seconds: 45,
          aspect_ratio: "9:16",
          video_type: "corporate",
        },
      ],
    }),
  });

  const data = await res.json();
  console.log(data.results.map((r) => `${r.uuid} → ${r.status}`));
  ```

  ```python Python theme={null}
  import requests

  resp = requests.post(
      "https://api.knowlify.com/v1/videos",
      headers={"X-API-Key": "kn_YOUR_KEY_HERE"},
      json={
          "videos": [
              {
                  "task": "Explain how HTTPS works in 30 seconds",
                  "video_duration_seconds": 60,
                  "aspect_ratio": "16:9",
                  "video_type": "explainer",
                  "color_palette": {"primary": "#0066cc", "accent": "#ff6600"},
              },
              {
                  "task": "Quarterly results recap",
                  "video_duration_seconds": 45,
                  "aspect_ratio": "9:16",
                  "video_type": "corporate",
              },
          ]
      },
      timeout=30,
  )
  resp.raise_for_status()
  for r in resp.json()["results"]:
      print(r["uuid"], r["status"])
  ```
</CodeGroup>

### Example success response

```json theme={null}
{
  "status": "ok",
  "total": 2,
  "enqueued": 2,
  "parked": 0,
  "errors": 0,
  "user_id": "5b9e...c1",
  "org_id": null,
  "results": [
    {
      "index": 0,
      "uuid": "550e8400-e29b-41d4-a716-446655440000",
      "session_id": "550e8400-e29b-41d4-a716-446655440001",
      "chat_url": "https://create.knowlify.com/p/chat/550e8400-e29b-41d4-a716-446655440001",
      "status": "queued"
    },
    {
      "index": 1,
      "uuid": "550e8400-e29b-41d4-a716-446655440002",
      "session_id": "550e8400-e29b-41d4-a716-446655440003",
      "chat_url": "https://create.knowlify.com/p/chat/550e8400-e29b-41d4-a716-446655440003",
      "status": "parked",
      "queue_position": 1
    }
  ]
}
```

## Next step

Each accepted item returns a `uuid`. Use it with [`GET /v1/videos/{uuid}`](/api-reference/poll-video) to track render progress until the job is complete.

## Limits & errors

* 30 requests / 60 seconds per caller. See [Rate Limits](/api-reference/rate-limits).
* 1–50 items per request; max 3 concurrent jobs per user/org (additional items are parked).
* Full status code reference: [Errors](/api-reference/errors).
