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

# Videos

> Make short social videos from code, pay per video, and post them to your connected accounts.

These endpoints drive the same video maker as **Dashboard → Create Video**: pick a template, give it
your website or a description (and, if you like, the script), and LumisReach writes, renders and
composites a social-ready MP4. When it is ready you can download it or post it to TikTok, Instagram,
Facebook, X and LinkedIn. To keep videos coming on a cadence, see [Video schedules](/api-reference/video-schedules).

| Endpoint | What it does | Scope |
| - | - | - |
| `POST /v1/videos/quote` | Price a video before making it | `videos:read` |
| `POST /v1/videos` | Make a video | `videos:write` |
| `GET /v1/videos/{id}` | Status, script, price, `video_url`, posts | `videos:read` |
| `GET /v1/videos` | Every video in the workspace, newest first | `videos:read` |
| `POST /v1/videos/{id}/approve` | Approve a written script (with edits) and render it | `videos:write` |
| `POST /v1/videos/{id}/retry` | Resume a failed video, free | `videos:write` |
| `DELETE /v1/videos/{id}` | Delete a video | `videos:write` |
| `GET /v1/videos/music` | The built-in royalty-free background tracks | `videos:read` |
| `GET /v1/social-connections` | The social accounts you can post to | `videos:read` |
| `POST /v1/videos/{id}/posts` | Post a finished video now, or schedule it | `videos:write` |
| `GET /v1/videos/{id}/posts` | A video's posts and their permalinks | `videos:read` |
| `DELETE /v1/posts/{id}` | Cancel a scheduled post | `videos:write` |

<Note>
  Videos need a plan that includes Content (the Growth Marketing Manager). Without it every endpoint here
  returns `403 plan_not_included`.
</Note>

## Pricing

Each video is priced at what the providers that make it charge us (presenter renders, generated shots
and pictures, narration, rendering) plus a small markup, and is **charged once**, at the moment it is
released to render:

* with your own `script`, when you create it;
* otherwise, when its written script is approved (automatically or by you).

Retries are free, and so are quotes, reads and posting. The charge comes out of your plan's included
video credits first, then your prepaid balance. If the workspace cannot pay for a video, the release is
refused with `400` and nothing is bought.

**Quote first.** `POST /v1/videos/quote` takes the same body as `POST /v1/videos` and returns the price,
without making anything:

```bash theme={null}
curl -X POST "https://api.lumisreach.com/api/v1/videos/quote" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "template": "talking_avatar", "target_duration_seconds": 30 }'
```

```json theme={null}
{
  "data": {
    "price_cents": 74,
    "price_usd": "$0.74",
    "estimated": true,
    "seconds": 30,
    "line_items": [
      { "label": "Research and writing", "cents": 6 },
      { "label": "Tavus avatar (30s)", "cents": 60 },
      { "label": "Rendering, subtitles and music", "cents": 8 }
    ]
  }
}
```

With your own `script` the quote is exact. Without one, the words (or shots, or slides) are not written
yet, so the quote is an estimate from `target_duration_seconds` (`estimated: true`) and the charge at
approval is for what was actually written. Every video also shows `price_cents` (what it costs now, or
was charged) and `billed_cents` (what it was charged, once released).

**Cap it.** Pass `max_price_cents` when you create a video. With your own script, a video above the cap
is rejected with `400` before anything is created. With a script approved automatically, one that comes
out above the cap is not approved: it waits in `script_review` with the reason in `error`, for you to
shorten and approve (or delete). When you approve by hand, pass `max_price_cents` to
`POST /v1/videos/{id}/approve` instead.

## Templates

`template` picks the recipe, and `preset` a style within it (omit it for the template's default).

| `template` | What it makes | Your own `script` |
| - | - | - |
| `talking_avatar` | A presenter over a capture of your site, a photo, your footage or a demo | Yes |
| `remotion` | Motion graphics with a narrator (`imessage`, `diagram`, `site-demo`) | No |
| `freeform_ai` | One AI-generated clip per scene, cut together, riding a viral trend | No |
| `meme_hook` | A few seconds of a viral clip, into your presenter | Yes |
| `interview` | A 2, 3 or 4-way split screen of avatars trading questions and answers | No |
| `phone_mockup` | A hand holding a phone, your video, site or photos on its screen | Yes |
| `slideshow` | Captioned still slides for a TikTok or Instagram carousel | No |
| `real_footage` | A real stock clip of a person, a caption over it, optional narrator | Yes |

Templates marked **No** write their own script scene by scene (or slide by slide), so sending `script`
with them is a `400`. The self-recorded style (where you film yourself) needs an upload in the
dashboard and is not available here.

Everything else the dashboard's Configure step sets is in the body: `format` (`vertical` or
`horizontal`), `target_duration_seconds`, the presenter (`avatar_provider`, `replica_id`, `avatar_id`,
`voice_id`, `avatar_layout`), the background (`background_mode`, `background_image`, `b_roll`),
subtitles, the video model for `freeform_ai`, slide settings for `slideshow`, the clip for
`real_footage` and `meme_hook`, and a site demo's `demo_focus` and `demo_login`. Settings a template does
not use are ignored. Presenters and voices can't be listed through the API yet: leave out
`replica_id` and `avatar_id` for the default presenter.

**Sources.** Give at least one of `source_url` (your website) or `source_text` (a description). Images
and videos must already be in LumisReach (`source_image_urls`, `source_video_urls`); there is no upload
endpoint in the API yet, so upload them in the dashboard or describe the business in `source_text`.

## Script up front, or review

```bash theme={null}
curl -X POST "https://api.lumisreach.com/api/v1/videos" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "talking_avatar",
    "source_url": "https://example.com",
    "script": "Tired of missed calls? Our AI receptionist answers every one, day or night.",
    "subtitle_style": "bold_words",
    "caption": "Never miss a call again",
    "hashtags": ["smallbusiness", "ai"],
    "music": { "library": "lumisreach", "track_id": "TRACK_ID" },
    "max_price_cents": 200
  }'
```

`TRACK_ID` is a `track_id` from `GET /v1/videos/music`.

* **Your own `script`**: spoken word for word. The video skips review, is charged, and starts
  rendering right away.
* **No `script`, `script_review: "auto"`** (the default): a script is written from your sources and
  approved as soon as it is written, which charges it (within `max_price_cents`).
* **No `script`, `script_review: "manual"`**: the video stops in `script_review`. Read the script (or
  `scenes`, `slides`, `interview`) with `GET /v1/videos/{id}` and its price in `price_cents`, then
  approve it with `POST /v1/videos/{id}/approve`, sending `{}` or your edits.

Either way the response is `201` with the video. Poll `GET /v1/videos/{id}`:

| `status` | Meaning |
| - | - |
| `queued` | Waiting for the pipeline to pick it up |
| `researching` | Reading your sources and writing the script |
| `script_review` | Written, waiting for approval (see `error` if an auto approval was held back) |
| `rendering` | Presenter, shots, slides or narration being made |
| `compositing` | Cutting it together, with subtitles and music |
| `ready` | Done: `video_url` and `thumbnail_url` are set |
| `failed` | `error` says why. `POST /v1/videos/{id}/retry` resumes it, free |

A video takes a few minutes; long ones and AI-generated scenes can take longer.

## Downloading

`video_url` and `thumbnail_url` are signed links to the finished files. They need no API key and don't
expire, so you can download the MP4 or hand the link to another service; treat them like a share link.

## Posting

List the accounts connected in **Dashboard → Connections**, then post:

```bash theme={null}
curl "https://api.lumisreach.com/api/v1/social-connections" -H "Authorization: Bearer YOUR_API_KEY"

curl -X POST "https://api.lumisreach.com/api/v1/videos/VIDEO_ID/posts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "connection_ids": ["CONNECTION_ID"],
    "captions": { "x": "Short one for X #ai" },
    "scheduled_for": "2026-10-02T16:00:00Z",
    "settings": { "tiktok": { "privacy_level": "public_to_everyone" } }
  }'
```

`caption` defaults to the video's caption and hashtags. Omit `scheduled_for` to post now. A slideshow
can go out as its slides with `"media_type": "carousel"` (TikTok and Instagram). Each account gets its
own post; follow them with `GET /v1/videos/{id}/posts`, and cancel one that is still scheduled with
`DELETE /v1/posts/{id}`. Posting is free.
