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

# Tools reference

> All 27 MCP tools, grouped by domain. Each maps 1:1 to an API operation.

The tools follow the production flow end to end:

**create → estimate → generate → review → render → publish.**

An agent can drive the whole pipeline with nothing but these tools and the
self-describing `get_schema` / `get_status`.

## Workflows

| Tool              | Maps to                     |
| ----------------- | --------------------------- |
| `list_workflows`  | `GET /v1/workflows`         |
| `get_workflow`    | `GET /v1/workflows/{id}`    |
| `create_workflow` | `POST /v1/workflows`        |
| `update_workflow` | `PUT /v1/workflows/{id}`    |
| `delete_workflow` | `DELETE /v1/workflows/{id}` |

`create_workflow` accepts a `shots` array and compiles it into grouped **scenes**
(an image → video pair per shot, wired and laid out) so you describe intent, not
node geometry.

## Assets

| Tool           | Maps to                   |
| -------------- | ------------------------- |
| `import_asset` | `POST /v1/storage/ingest` |

`import_asset({ url, kind })` pulls an external **image / video / music** URL into
Lavendly so you can use it in a workflow (e.g. as an `imageSource`). The server
downloads it and enforces the **same limits as the app uploader** - size (image
25 MB / video 300 MB / music 50 MB), duration (video 3 min / music 10 min), and
your storage quota - then returns the hosted URL. It is **URL-only**: local files
on the user's machine must be uploaded in the app (they stream straight to
storage there), not through the MCP.

## Generate

| Tool                    | Maps to                                    |
| ----------------------- | ------------------------------------------ |
| `estimate_cost`         | `GET /v1/workflows/{id}/cost-estimate`     |
| `generate_scene`        | `POST /v1/workflows/{id}/generate`         |
| `get_generation_status` | `GET /v1/workflows/{id}/generation-status` |

`estimate_cost` returns `{ total, remaining, per_scene, available_credits,
sufficient }` so you can state the price and get approval **before** spending.
`generate_scene` generates one scene (`node_id`) or every ungenerated scene
(omit `node_id` → the default is the whole video); it charges per clip and
stitches nothing. Poll `get_generation_status` for the narratable phase +
progress and the per-scene artifacts as they land.

<Tip>
  Render is **free** after the scenes are generated. Generation is the only
  charged step; `create_render` just stitches the clips you already paid for and
  reuses anything already generated, so it never re-charges.
</Tip>

## Audio

| Tool                    | Maps to                                          |
| ----------------------- | ------------------------------------------------ |
| `get_workflow_audio`    | `GET /v1/workflows/{id}/audio`                   |
| `set_clip_native_audio` | `PATCH /v1/workflows/{id}/clips/{clip_id}/audio` |
| `attach_track`          | `POST /v1/workflows/{id}/clips/{clip_id}/tracks` |
| `update_track`          | `PATCH /v1/workflows/{id}/tracks/{track_id}`     |
| `detach_track`          | `DELETE /v1/workflows/{id}/tracks/{track_id}`    |

## Render

| Tool               | Maps to                                   |
| ------------------ | ----------------------------------------- |
| `create_render`    | `POST /v1/workflows/{id}/renders`         |
| `get_render`       | `GET /v1/workflows/{id}/renders/{job_id}` |
| `list_renders`     | `GET /v1/workflows/{id}/renders`          |
| `list_all_renders` | `GET /v1/renders`                         |

`create_render` is **reuse-aware**: scenes that already have a clip are folded
into the stitch untouched and never re-charged. A render that fails after
producing some clips charges only for the clips delivered (the rest is
released), never a blanket refund of work that already cost upstream.

## Review

| Tool              | Maps to                                 |
| ----------------- | --------------------------------------- |
| `quality_check`   | `POST /v1/workflows/{id}/quality-check` |
| `get_scene_frame` | `POST /v1/workflows/{id}/scene-frame`   |

`quality_check` runs ffprobe sanity (duration, video + audio streams) plus a
vision rubric that catches AI "tells" (waxy skin, melting hands, identity
drift) and returns `{ pass, score, failures, reroll_hint }`. If `pass` is
false, regenerate the scene with `generate_scene` and append `reroll_hint` to
the prompt. `get_scene_frame` returns a still-frame thumbnail URL so a human (or
a multimodal agent) can eyeball a scene before rendering. Both take an optional
`node_id`; omit it to target the final video.

## Publish

| Tool             | Maps to                                         |
| ---------------- | ----------------------------------------------- |
| `list_channels`  | `GET /v1/channels`                              |
| `publish_video`  | `POST /v1/workflows/{id}/publish`               |
| `schedule_video` | `POST /v1/workflows/{id}/publish` (with `when`) |

`list_channels` returns the connected social channels (or `connected: false` - tell the user to connect one in Settings; an agent can't). `publish_video` posts
the rendered video now; `schedule_video` posts it at a future `when` (ISO 8601)
via Lavendly's own scheduler. Platform AI-disclosure is set automatically (e.g.
TikTok), and the media upload + post payload are built server-side, so you only
pass `{ channel_id, caption?, title?, when? }`.

<Warning>
  `publish_video` posts publicly and is hard to undo. Confirm the channel and
  content with the user first.
</Warning>

## Capabilities & ledger

| Tool                | Maps to                 |
| ------------------- | ----------------------- |
| `get_ledger`        | `GET /v1/ledger`        |
| `get_monthly_usage` | `GET /v1/usage/monthly` |
| `get_status`        | `GET /v1/_status`       |
| `get_schema`        | `GET /v1/_schema`       |

## Idempotency from MCP

`create_workflow`, `attach_track`, `create_render`, `generate_scene`, and
`publish_video` accept an `idempotency_key` argument. The MCP server forwards it
on the underlying HTTP request; the same key within 5 minutes returns the same
response, byte-for-byte.

Use this whenever an agent might retry a tool call after a timeout: generate the
key once per logical action (e.g. `${workflow_id}:${date}`) and pass it on every
retry.

## Sample agent prompt

> Using Lavendly:
>
> 1. Check my ledger; bail out if I have less than 50 credits available.
> 2. Create a workflow called *Bookshop fox* with three shots:
>    a sleepy fox finding an old map, the map glowing, the fox stepping
>    through a door of light. 5 s each.
> 3. `estimate_cost` and tell me the price; wait for my go-ahead.
> 4. `generate_scene` for the whole video. Poll `get_generation_status` and
>    tell me each scene as it lands.
> 5. `quality_check` every scene. If any scene fails, regenerate it with the
>    `reroll_hint`. Send me a `get_scene_frame` thumbnail of each.
> 6. Render it (free), poll until done, send me the video URL.
> 7. List my channels and, once I confirm, schedule it to YouTube for 9am
>    tomorrow.

The agent needs no other context: the tool descriptions are self-explanatory and
`get_status` tells it which providers are wired up.
