LLM / AI
AI-powered endpoints for generating content, running quality checks, and preprocessing prompts. All routes pass through a general rate limiter (120 req/min by default). Generation and analysis endpoints have additional stricter limiters.
Endpoints overview​
| Method | Endpoint | Permission | Description |
|---|---|---|---|
POST | /api/llm/ideas | llm:generate | Generate story ideas |
POST | /api/llm/alternatives | llm:generate | Generate alternatives |
POST | /api/llm/prompt-preprocess | authenticated | Preprocess prompt |
POST | /api/llm/generate-scenes | llm:generate | Generate episode scenes |
GET | /api/llm/canon-check/:episodeId | authenticated | Get canon check result |
POST | /api/llm/canon-check | llm:analyze | Run canon check |
GET | /api/llm/continuity-check/:episodeId | authenticated | Get continuity check result |
POST | /api/llm/continuity-check | llm:analyze | Run continuity check |
GET | /api/llm/safety-check/:episodeId | authenticated | Get safety check result |
POST | /api/llm/safety-check | llm:analyze | Run safety check |
POST | /api/llm/final-approve | llm:approve | Final approve episode |
Content Generation​
Generate story ideas​
POST /api/llm/ideas — Auth required, Rate limited, Permission: llm:generate
Request body
{
"projectId": "proj-uuid-001",
"userPrompt": "The heroes discover a hidden door behind the waterfall",
"scene": {
"id": 3,
"title": "The Cave",
"mood": "mysterious",
"blocks": [
{ "type": "action", "text": "They enter the cave cautiously" }
]
},
"includeContext": true
}
| Field | Required | Notes |
|---|---|---|
projectId | yes | UUID of the project (used to load characters/world rules for context) |
userPrompt | yes | The writer's idea prompt |
scene | yes | Current scene object with blocks |
includeContext | no | Include broader episode context; defaults to false |
Response — 200
{
"ideas": [
"Mia notices ancient symbols carved into the stone",
"A soft glow emanates from behind a moss-covered wall",
"The sound of rushing water grows louder as they move deeper"
]
}
Generate alternatives​
POST /api/llm/alternatives — Auth required, Rate limited, Permission: llm:generate
Suggests alternative phrasings or directions for existing blocks in a scene.
Request body
{
"projectId": "proj-uuid-001",
"scene": {
"id": 3,
"title": "The Cave",
"blocks": [
{ "type": "dialog", "text": "We need to leave. Now.", "characterId": "char-uuid-001" }
]
},
"userPrompt": "Make it more urgent and emotional"
}
| Field | Required | Notes |
|---|---|---|
projectId | yes | UUID of the project |
scene | yes | Current scene with blocks |
userPrompt | no | Optional direction hint |
Response — 200
{
"alternatives": [
"Run! We can't stay here another second!",
"There's no time — go, go, go!",
"Something's wrong. We have to get out right now."
]
}
Preprocess prompt​
POST /api/llm/prompt-preprocess — Auth required, Rate limited
Cleans and enriches a raw user-written prompt before using it for image or video generation.
Request body
{
"prompt": "girl in forest with big tree",
"projectId": "proj-uuid-001",
"target": "image",
"context": "generic"
}
| Field | Required | Notes |
|---|---|---|
prompt | yes | Raw prompt text |
projectId | no | If provided, project-specific style settings are applied |
target | no | image | video; defaults to "image" |
context | no | generic | character | location; defaults to "generic" |
Response — 200
{
"processedPrompt": "A young girl standing in a lush forest, beside an ancient towering oak tree, warm afternoon light filtering through the leaves, cinematic, photorealistic"
}
Generate episode scenes​
POST /api/llm/generate-scenes — Auth required, Rate limited, Permission: llm:generate
Generates a complete set of scenes (with blocks) for an episode and persists them to the database.
Request body
{
"projectId": "proj-uuid-001",
"episodeId": "ep-uuid-001",
"prompt": "The heroes discover the villain's hidden base and must escape before the alarm goes off",
"sceneCount": 3
}
| Field | Required | Notes |
|---|---|---|
projectId | yes | UUID of the project |
episodeId | yes | UUID of the episode to populate |
prompt | yes | Episode synopsis (min 5 chars) |
sceneCount | no | Number of scenes to generate; defaults to 3 |
Response — 200
{ "success": true }
Quality Checks​
All check endpoints come in a GET (retrieve cached result) and POST (trigger a new analysis) pair. Results are persisted in the database.
Canon check​
GET /api/llm/canon-check/:episodeId — Auth required
Returns the most recently saved canon check result for the episode.
Response — 200
{
"episodeId": "ep-uuid-001",
"checkedAt": "2025-04-10T12:00:00.000Z",
"passed": false,
"issues": [
{
"sceneId": 3,
"blockId": "block-uuid-007",
"description": "Mia uses a smartphone, but world rules state no electronics exist in this era.",
"severity": "high"
}
]
}
POST /api/llm/canon-check — Auth required, Rate limited, Permission: llm:analyze
Runs a new canon check against all project world rules and saves the result.
Request body
{ "episodeId": "ep-uuid-001" }
Response — 200 — same shape as GET result above
Continuity check​
GET /api/llm/continuity-check/:episodeId — Auth required
Returns the most recently saved continuity check result.
Response — 200
{
"episodeId": "ep-uuid-001",
"checkedAt": "2025-04-10T12:05:00.000Z",
"passed": true,
"issues": []
}
POST /api/llm/continuity-check — Auth required, Rate limited, Permission: llm:analyze
Checks for internal contradictions within the episode (character state, timeline, locations).
Request body
{ "episodeId": "ep-uuid-001" }
Response — 200 — same shape as GET result above
Safety check​
GET /api/llm/safety-check/:episodeId — Auth required
Returns the most recently saved safety check result.
Response — 200
{
"episodeId": "ep-uuid-001",
"checkedAt": "2025-04-10T12:10:00.000Z",
"passed": true,
"score": 98,
"issues": []
}
POST /api/llm/safety-check — Auth required, Rate limited, Permission: llm:analyze
Checks episode content for age-appropriateness and policy violations based on the project's target audience settings.
Request body
{ "episodeId": "ep-uuid-001" }
Response — 200 — same shape as GET result above
Approval​
Final approve​
POST /api/llm/final-approve — Auth required, Rate limited, Permission: llm:approve
Runs canon, continuity, and safety checks in sequence. If all pass, marks the episode as approved and creates a completion task for the authenticated user.
Request body
{ "episodeId": "ep-uuid-001" }
Response — 200
{
"approved": true,
"episodeId": "ep-uuid-001",
"canonCheck": { "passed": true },
"continuityCheck": { "passed": true },
"safetyCheck": { "passed": true, "score": 98 }
}