Film Director
Film Director is a chat-based tool that generates a storyboard from a creative concept. The AI writes a shot-by-shot breakdown with timing, direction, and dialogue. After generation, you can refine the storyboard inline and persist it as a Filmwork project. The resulting project uses the full Filmwork pipeline for shot production, asset uploads, and roll generation.
Flow overview
| Step | Action | Cost | Endpoint |
|---|---|---|---|
| 1 | Generate storyboard | 1-2 credits | POST /api/chat/stream with activeTool: "film_director" |
| 2a | Get refine suggestions (optional) | 1 credit | POST /api/filmwork/director/refine-suggestions |
| 2b | Refine storyboard (optional) | 1 credit | POST /api/filmwork/director/refine (SSE stream) |
| 3 | Persist as Filmwork project | 0-1 credits | POST /api/filmwork/director/persist |
Persist costs 0 credits if storyboard labels are correctly formatted, 1 credit if they need AI repair.
Step 1: Generate storyboard
curl -N https://narrativelion.com/api/chat/stream \
-H "Authorization: Bearer nlk_your_key" \
-H "Content-Type: application/json" \
-d '{
"threadId": "your-thread-uuid",
"actionId": "unique-action-uuid",
"event": {
"type": "user_text",
"payload": {
"text": "A delivery robot navigates a cyberpunk city at night",
"activeTool": "film_director",
"filmDirectorVideoType": "cinematic",
"filmDirectorTargetDurationSec": 30,
"filmDirectorAspectRatio": "9:16",
"filmDirectorVisualStyle": "Neon noir, rain-slicked streets",
"filmDirectorPinnedNoteIds": ["note-uuid-1"],
"filmDirectorInstruction": "Focus on loneliness and scale contrast"
}
}
}'Payload fields
| Name | Type | Required | Description |
|---|---|---|---|
activeTool | "film_director" | Required | Must be "film_director". |
filmDirectorVideoType | enum | Required | "short" | "cinematic" | "animate" |
filmDirectorTargetDurationSec | number | Required | Total video duration in seconds (positive). |
filmDirectorAspectRatio | string | Required | e.g. "9:16", "16:9", "1:1" |
filmDirectorInstruction | string | Optional | Creative brief. Falls back to text field if omitted. |
filmDirectorVisualStyle | string | Optional | Visual style guidance (e.g. "Neon noir"). |
filmDirectorPinnedNoteIds | string[] | Optional | Note IDs to use as context (max 20). IDs must belong to the account. |
SSE response
The stream emits token events with the storyboard markdown, then a complete event. The storyboard draft is in finalMessage. The filmDirectorSetup artifact echoes back the resolved setup — pass it to refine/persist calls.
event: complete
data: {
"finalMessage": "# Cyberpunk Delivery\n\n**Summary:** 1 scene, 4 shots, ~30s.\n\n**01A** (5s) — Wide establishing\nRain-soaked city street...",
"artifacts": {
"filmDirectorSetup": {
"videoType": "cinematic",
"targetDurationSec": 30,
"aspectRatio": "9:16",
"visualStyle": "Neon noir, rain-slicked streets",
"pinnedNoteIds": ["note-uuid-1"],
"instruction": "Focus on loneliness and scale contrast"
},
"ragSources": [
{ "noteId": "note-uuid-1", "noteTitle": "Cyberpunk Reference", "score": 0.85 }
]
}
}The draft is not saved until you call persist. The storyboard markdown in finalMessage follows the Filmwork storyboard format (**01A** (4s) — Title).
Step 2a: Get refine suggestions (optional)
curl -X POST https://narrativelion.com/api/filmwork/director/refine-suggestions \
-H "Authorization: Bearer nlk_your_key" \
-H "Content-Type: application/json" \
-d '{
"storyboard": "<the storyboard markdown from finalMessage>",
"setup": { "videoType": "cinematic", "targetDurationSec": 30, ... }
}'| Name | Type | Required | Description |
|---|---|---|---|
storyboard | string | Required | Current storyboard markdown (max 50,000 chars). |
setup | object | Required | The filmDirectorSetup object from the generate step. |
{
"suggestions": [
{ "label": "Add vulnerability moment", "prompt": "Insert a close-up showing..." },
{ "label": "End on recipient reveal", "prompt": "Change the final shot to..." }
]
}Step 2b: Refine storyboard (optional)
Streams a fully revised storyboard markdown (not a diff). Does not persist.
curl -N -X POST https://narrativelion.com/api/filmwork/director/refine \
-H "Authorization: Bearer nlk_your_key" \
-H "Content-Type: application/json" \
-d '{
"currentStoryboard": "<current storyboard markdown>",
"setup": { "videoType": "cinematic", "targetDurationSec": 30, ... },
"refinementPrompt": "Add a close-up of the robot's face in shot 02A",
"pinnedNoteIds": ["note-uuid-1"]
}'| Name | Type | Required | Description |
|---|---|---|---|
currentStoryboard | string | Required | Current storyboard markdown. |
setup | object | Required | The filmDirectorSetup object from the generate step. |
refinementPrompt | string | Required | What to change. |
pinnedNoteIds | string[] | Optional | Note IDs for additional context. |
event: token
data: {"content":"# Cyberpunk Delivery (revised)\n\n..."}
event: complete
data: {}Accumulate token events to get the full revised storyboard. Save it via updateNote or pass to persist.
Step 3: Persist as Filmwork project
Creates a Filmwork note + shot records from the storyboard. Labels are validated against the storyboard format. Malformed labels trigger an AI repair pass (1 credit).
curl -X POST https://narrativelion.com/api/filmwork/director/persist \
-H "Authorization: Bearer nlk_your_key" \
-H "Content-Type: application/json" \
-d '{
"draftMarkdown": "# Cyberpunk Delivery\n\n**01A** (5s) — Wide establishing\nRain-soaked city...",
"setup": {
"videoType": "cinematic",
"targetDurationSec": 30,
"aspectRatio": "9:16",
"instruction": "A delivery robot navigates a cyberpunk city"
}
}'| Name | Type | Required | Description |
|---|---|---|---|
draftMarkdown | string | Required | Storyboard markdown from the generate/refine step. |
setup | object | Required | The filmDirectorSetup object. See setup schema below. |
{
"noteId": "new-filmwork-note-uuid",
"title": "Cyberpunk Delivery",
"generatedShots": [],
"totalShots": 4,
"remainingShots": 4
}| Name | Type | Required | Description |
|---|---|---|---|
noteId | string | Required | New Filmwork note UUID. |
title | string | Optional | Extracted from the first heading of the storyboard. |
totalShots | number | Required | Number of shot labels parsed from the storyboard. |
remainingShots | number | Required | Shots not yet populated with direction/prompts. |
Error codes
| HTTP | Code | Description |
|---|---|---|
400 | INVALID_STORYBOARD_FORMAT | No shot labels found after AI repair attempt. |
400 | INVALID_PINNED_NOTE | A pinned note ID was not found for this account. |
429 | QUOTA_EXCEEDED | Credit limit reached for this billing period. |
Setup schema
Used in generate payload fields, refine, and persist. The filmDirectorSetup artifact from the generate step returns this exact shape.
| Name | Type | Required | Description |
|---|---|---|---|
videoType | enum | Required | "short" | "cinematic" | "animate" |
targetDurationSec | number | Required | Total duration in seconds (positive). |
aspectRatio | string | Required | e.g. "9:16", "16:9", "1:1" |
instruction | string | Required | Creative brief / concept description. |
visualStyle | string | Optional | Visual style guidance. |
pinnedNoteIds | string[] | Optional | Note IDs for context (max 20). Default []. |
Important notes
- Generate uses the
chatscope. Refine usesnotes:read. Persist usesnotes:write. - The storyboard uses Filmwork label format:
**01A** (4s) — Title. Write correct labels to avoid the 1-credit repair cost. - After persisting, use Filmwork GraphQL operations to manage shots, upload assets, and generate rolls.
- Pinned note IDs are validated on both generate and refine — invalid IDs return an error.