# Lumen5 Video Creation API

Create Lumen5 videos from your content. Send a script or some source text, and Lumen5's agent turns it into a finished video in one of your workspaces. You can leave the style to the agent or pin down the template, voice, brand kit, aspect ratio and length yourself.

There are three ways to connect:

- **HTTP**: call the API directly from your own code. Most of this page documents it, starting with the quick start below.
- **Automation platforms**: use Lumen5's apps on platforms like Make, Zapier and n8n to create videos from your workflows, without writing code. See "Automation platforms" below.
- **MCP**: connect an AI assistant such as Claude, ChatGPT or Cursor, and it can create videos for you as a tool. See "MCP" below.

## Quick start

1. Create an API key at `https://lumen5.com/dashboard/api/`. It is shown once, so store it somewhere safe.
2. Find the workspace to create the video in:

   ```bash
   curl https://lumen5.com/api/v1/video-creation/workspaces/ \
     -H "Authorization: Bearer $LUMEN5_API_KEY"
   ```

3. Ask for a video:

   ```bash
   curl -X POST https://lumen5.com/api/v1/video-creation/requests/ \
     -H "Authorization: Bearer $LUMEN5_API_KEY" \
     -H "Content-Type: application/json" \
     -H "Idempotency-Key: $(uuidgen)" \
     -d '{
       "workspace": "ws_123",
       "input_content": {"text": "Paste an article, notes or a brief here."},
       "output_settings": {"aspect_ratio": "landscape", "target_duration_seconds": 60}
     }'
   ```

4. Poll `GET /api/v1/video-creation/requests/{request_id}/` until `status` is `completed` or `failed`, or register a webhook endpoint to be told. A completed request has a `video_url` that opens the video in Lumen5.

## Automation platforms

Lumen5 publishes apps on automation platforms, so you can create videos from your workflows without writing code: for example, turning each new blog post into a video.

| Platform | Status |
| --- | --- |
| Make | Coming soon |
| Zapier | Coming soon |
| n8n | Coming soon |

Each app connects to Lumen5 with OAuth: you sign in to Lumen5 and approve access when you add the connection, so there's no API key to create or paste. The apps are built on this API, so the options and behavior on this page apply to them too.

Until the app for your platform is available, you can use the platform's own HTTP step to call the API directly with an API key, as in the quick start above.

## MCP

Lumen5 runs a Model Context Protocol (MCP) server, so AI assistants and agents can create videos as a tool. Its URL is:

```
https://lumen5.com/mcp
```

It speaks MCP's Streamable HTTP transport. Its tools work like the HTTP endpoints below, so the options, limits and errors on this page apply to it too.

### Connect with OAuth

Most assistants (Claude, ChatGPT, Cursor, VS Code and others) only need the URL. Add `https://lumen5.com/mcp` as a custom connector or remote MCP server, and the assistant opens Lumen5 for you to sign in and approve access. Approve both permissions if you want it to create videos, not just look things up.

Lumen5 doesn't review the assistants that connect this way, so the approval screen marks them as unverified and shows where approving sends you. Only approve a connection you just started yourself.

Under the hood, the server follows the MCP authorization spec. A call without a credential gets a `401` whose `WWW-Authenticate` header points to `https://lumen5.com/.well-known/oauth-protected-resource/mcp`, which names Lumen5's authorization server (metadata at `https://lumen5.com/.well-known/oauth-authorization-server`). Clients register themselves, either with a client ID metadata document or through dynamic client registration at `/oauth/register/`, then sign the user in with the authorization-code flow and PKCE.

### MCP tools

| Tool | What it does |
| --- | --- |
| `list_workspaces` | Lists the workspaces you can create videos in. |
| `list_recipes`, `list_templates`, `list_voices`, `list_blueprints`, `list_brand_kits` | List the style options, as the matching endpoints do. |
| `preview_video_request` | Checks a request without creating anything, like `POST /resolve/`. |
| `create_video` | Starts a video, like `POST /requests/`. Needs `video:write`. |
| `get_video_request` | Gets a request's status and, once it's done, its `video_url`. |
| `list_video_requests` | Lists a workspace's requests, newest first. |

`preview_video_request` and `create_video` take the same body as `POST /requests/`, and `create_video` also takes an optional `idempotency_key` in place of the header. Lists return up to 50 results per `page`. When a call fails, the tool's result says why, so the assistant can correct itself.

## Authentication

Send a credential on every call as `Authorization: Bearer <token>`. Either works:

- **API key**: starts with `lm5_sk_`. Create and delete keys at `https://lumen5.com/dashboard/api/`. A key acts as the user who created it.
- **OAuth 2.0 access token**: for apps acting on behalf of other Lumen5 users.

What a credential may do depends on its scopes:

| Scope | Allows |
| --- | --- |
| `basic:read` | Every `GET`, and `POST /resolve/` (a dry run that changes nothing) |
| `video:write` | Creating video requests and managing webhook endpoints |

Workspace viewers and reviewers can read but not create videos, just as in the Lumen5 app.

## Workspaces

Every call that reads or creates videos names the workspace it acts in: in the `workspace` query parameter for `GET`, and in the `workspace` body field for `POST`. There is no default workspace. Get workspace ids from `GET /workspaces/`.

## IDs

Every id the API returns has a prefix that says what it is. Pass ids back exactly as you received them.

| Prefix | Names | Listed by |
| --- | --- | --- |
| `ws_` | Workspace | `GET /workspaces/` |
| `tpl_` | Template | `GET /templates/` |
| `voice_` | AI voice | `GET /voices/` |
| `bp_` | Blueprint (a workspace's saved video style) | `GET /blueprints/` |
| `bk_` | Brand kit | `GET /brand-kits/` |
| `req_` | Video creation request | `GET /requests/` |
| `whep_` | Webhook endpoint | `GET /webhook-endpoints/` |

Recipe ids have no prefix (for example `create-promo-ad`); get them from `GET /recipes/`.

## Pagination

List endpoints return 50 items per page:

```json
{
  "count": 120,
  "next": "https://lumen5.com/api/v1/video-creation/templates/?page=2&workspace=ws_123",
  "previous": null,
  "results": []
}
```

Follow `next` until it is `null`.

## Choosing a style

Everything in `output_settings` is optional. Anything you leave out (or send as `null`) is chosen by the agent.

There are two ways to set a style:

- **A blueprint** (`blueprint`): a workspace's saved style. It decides the template, voice, recipe, brand kit, aspect ratio and duration, so it can't be combined with any of them. `video_name` can still be set.
- **Individual settings**: any combination of `template`, `voice`, `recipe`, `brand_kit`, `aspect_ratio` and `target_duration_seconds`.

Every listing endpoint accepts `search` to narrow the list by keywords, and only returns options the named workspace can use.

Use `POST /resolve/` to check a body before sending it: it validates exactly as `POST /requests/` does and returns what would be stored, without creating anything.

## Endpoints

All paths are relative to `https://lumen5.com`.

### List workspaces

`GET /api/v1/video-creation/workspaces/`

The workspaces the caller can create videos in. Each result: `id` (`ws_…`), `label`.

### List recipes

`GET /api/v1/video-creation/recipes/?workspace=ws_123&search=promo`

The kinds of video a request can ask for. Each result: `id`, `label`, `description`.

### List voices

`GET /api/v1/video-creation/voices/?workspace=ws_123`

AI voices for narration. Each result: `id` (`voice_…`), `label`, `attributes` (for example accent and gender, each a list of strings), `preview_url`, `is_paid`.

### List templates

`GET /api/v1/video-creation/templates/?workspace=ws_123`

Each result: `id` (`tpl_…`), `label`, `description`, `aspect_ratios`, `preview_url`.

### List blueprints

`GET /api/v1/video-creation/blueprints/?workspace=ws_123`

Each result: `id` (`bp_…`), `label`, `description`, `aspect_ratio`.

### List brand kits

`GET /api/v1/video-creation/brand-kits/?workspace=ws_123`

Each result: `id` (`bk_…`), `label`.

### Validate a request (dry run)

`POST /api/v1/video-creation/resolve/`

Takes the same body as creating a request. Returns `200` with the request as it would be stored (`workspace`, `input_content`, `output_settings`, `instructions`, with `null` for anything left to the agent), or `400` explaining what's wrong. Needs only `basic:read`.

### Create a video

`POST /api/v1/video-creation/requests/`

Body:

```json
{
  "workspace": "ws_123",
  "input_content": {
    "script": null,
    "text": "Source material for the agent to write a script from."
  },
  "output_settings": {
    "blueprint": null,
    "template": "tpl_abc",
    "voice": "voice_emma",
    "recipe": "create-promo-ad",
    "brand_kit": "bk_42",
    "aspect_ratio": "vertical",
    "target_duration_seconds": 45,
    "video_name": "Q3 product update"
  },
  "instructions": "Upbeat tone. End on a call to action to book a demo."
}
```

| Field | Required | Description |
| --- | --- | --- |
| `workspace` | Yes | A `ws_` id. |
| `input_content.script` | See below | Narration, used word for word. |
| `input_content.text` | See below | Source material (an article, notes, a brief) for the agent to write a script from. |
| `output_settings.blueprint` | No | A `bp_` id. Can't be combined with the settings below, apart from `video_name`. |
| `output_settings.template` | No | A `tpl_` id. |
| `output_settings.voice` | No | A `voice_` id. |
| `output_settings.recipe` | No | A recipe id. |
| `output_settings.brand_kit` | No | A `bk_` id. |
| `output_settings.aspect_ratio` | No | `landscape`, `square`, `vertical` or `fourfive` (4:5). |
| `output_settings.target_duration_seconds` | No | 5 to 600. |
| `output_settings.video_name` | No | Up to 200 characters. |
| `instructions` | See below | Free-text direction for the agent. |

Send at least one of `script`, `text` and `instructions`, and never both `script` and `text`. With `instructions` alone, the agent writes the script from them.

Responses:

- `201`: the new video request (see "The video request object" below).
- `200`: a retry of an earlier request with the same `Idempotency-Key`; returns that request instead of making another video.
- `400`: the body is invalid. The response says which fields are wrong and why.
- `409`: the `Idempotency-Key` was already used with a different body.
- `429`: too many requests (see "Limits" below).

**Idempotency.** Send an `Idempotency-Key` header (any unique string up to 255 characters, such as a UUID) so you can safely retry after a timeout or network error. Retrying with the same key and body returns the original request; the same key with a different body is refused with `409`. Keys are scoped to the caller and workspace.

### List video requests

`GET /api/v1/video-creation/requests/?workspace=ws_123&status=running`

The workspace's requests, newest first. `status` is optional: `running`, `completed` or `failed`.

### Get a video request

`GET /api/v1/video-creation/requests/{request_id}/`

### List webhook endpoints

`GET /api/v1/video-creation/webhook-endpoints/`

### Add a webhook endpoint

`POST /api/v1/video-creation/webhook-endpoints/`

```json
{
  "url": "https://example.com/lumen5-webhook",
  "events": ["request.completed", "request.failed"]
}
```

`url` must be a public `https` URL. Returns `201` with `id` (`whep_…`), `url`, `events`, `created_at` and `secret` (`whsec_…`). The secret is shown only this once; store it to verify deliveries.

An endpoint receives events for all of the caller's requests, in every workspace.

### Delete a webhook endpoint

`DELETE /api/v1/video-creation/webhook-endpoints/{endpoint_id}/`

Returns `204`.

### Rotate a webhook secret

`POST /api/v1/video-creation/webhook-endpoints/{endpoint_id}/rotate-secret/`

Returns the endpoint with its new `secret`. The old secret keeps working for 24 hours, so you can update your receiver without dropping deliveries.

### Get the OpenAPI schema

`GET /api/v1/video-creation/schema/`

No authentication needed. Use it to generate a client or to give an agent the full set of operations.

## The video request object

```json
{
  "id": "req_9f1c2d3e-…",
  "status": "completed",
  "workspace": "ws_123",
  "input_content": {"script": null, "text": "Source material…"},
  "output_settings": {
    "blueprint": null,
    "template": "tpl_abc",
    "voice": null,
    "recipe": null,
    "brand_kit": null,
    "aspect_ratio": "vertical",
    "target_duration_seconds": null,
    "video_name": null
  },
  "instructions": null,
  "video_url": "https://lumen5.com/app/?id=456",
  "error": null,
  "created_at": "2026-10-06T18:30:00Z",
  "finished_at": "2026-10-06T18:34:10Z"
}
```

| Field | Description |
| --- | --- |
| `status` | `running`, then `completed` or `failed`. |
| `output_settings` | What you asked for. `null` means you left it to the agent. |
| `video_url` | Opens the video in Lumen5. `null` until there is a video. |
| `error` | Why the request failed, when `status` is `failed`. |
| `finished_at` | When the request completed or failed. |

## Webhooks

Lumen5 sends a `POST` with a JSON body to each endpoint subscribed to the event:

| Event | Sent when |
| --- | --- |
| `request.completed` | A video request finished and its video is ready. |
| `request.failed` | A video request failed. |

```json
{
  "event": "request.completed",
  "request_id": "req_9f1c2d3e-…",
  "workspace": "ws_123",
  "status": "completed",
  "occurred_at": "2026-10-06T18:34:10+00:00",
  "video_url": "https://lumen5.com/app/?id=456"
}
```

Payloads say what happened and, once there is a video, link to it in `video_url` (`null` otherwise, as for a failed request). Verify the signature before you act on a delivery or pass its `video_url` on. Fetch `GET /requests/{request_id}/` with your own credentials for the rest of the details.

Respond with a `2xx` status within 10 seconds. Redirects aren't followed. Respond with `410 Gone` to unsubscribe: the endpoint is deleted and gets no further deliveries. Any other response, or no response, is retried up to 5 times over about half an hour, so the same event can arrive more than once: use `request_id` and `event` to ignore repeats.

Each delivery carries these headers:

| Header | Value |
| --- | --- |
| `X-Lumen5-Event` | The event name. |
| `X-Lumen5-Timestamp` | When the delivery was signed. |
| `X-Lumen5-Signature` | One or more comma-separated signatures, each `v0=` followed by a hex HMAC-SHA256 signature. |

To verify a delivery, compute HMAC-SHA256 over `v0:{timestamp}:{raw request body}` with the endpoint's secret, and accept the delivery if it matches any of the signatures in the header, comparing in constant time. Use the raw body bytes, before any JSON parsing.

The header usually holds one signature. For 24 hours after you rotate the secret, it holds one for the new secret and one for the old, so a receiver with either secret keeps verifying deliveries.

```python
import hashlib
import hmac


def is_from_lumen5(secret: str, timestamp: str, raw_body: str, signature_header: str) -> bool:
    expected = "v0=" + hmac.new(secret.encode(), f"v0:{timestamp}:{raw_body}".encode(), hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(expected, signature) for signature in signature_header.split(","))
```

## Errors

Errors use standard HTTP status codes with a JSON body:

| Status | Meaning |
| --- | --- |
| `400` | The body or parameters are invalid. The body maps each bad field to a list of messages, and `detail` sums them up in one readable string. |
| `401` | The credential is missing, invalid, revoked or expired. |
| `403` | The credential lacks the scope, or the user's role can't make that change. |
| `404` | The workspace, request or endpoint doesn't exist or isn't visible to the caller. |
| `409` | The `Idempotency-Key` was already used with a different body. |
| `429` | Rate limited. Wait for the number of seconds in the `Retry-After` header. |

## Limits

Each user can create up to 300 video requests an hour, on top of Lumen5's general API rate limits.

## Notes for AI agents

- If you can use MCP tools, connect to `https://lumen5.com/mcp` instead of calling HTTP. See "MCP" above.
- Fetch this page as Markdown from `https://lumen5.com/developers/api.md`, and the machine-readable spec from `https://lumen5.com/api/v1/video-creation/schema/?format=json`.
- Start with `GET /workspaces/`. If there is more than one workspace, ask the user which to use.
- Only use ids returned by the listing endpoints. Don't guess them.
- Prefer leaving style settings out unless the user asked for them; the agent picks sensible defaults.
- Call `POST /resolve/` before `POST /requests/` to catch mistakes without creating anything.
- Always send an `Idempotency-Key`, and reuse it when retrying the same request.
- Creating a video takes minutes. Poll no more often than every 15 seconds, or use a webhook.
