# Create a film from a briefing

`POST https://api.cream-ai.de/api/v1/films`

Creates a film from a briefing in mint and, depending on `automation`, starts the automatic production.
The film is always created with the settings of its template (`template`), the previous version and `settings` (in this order, `settings` wins).

**Same order, same film.** With `video_key`, the company-wide identity of a film is `video_key` + `version`.
Sending the same order again (same `title`, `narration_text`, `user_email`, `language`, `template`, `settings`, `category` and `automation`) returns `200` with the existing film, even after the Idempotency-Key expired and with any key; nothing is created or charged again.
`metadata` is not part of this comparison and is never changed by a repeated order.
The same `video_key` and `version` with other content returns `409 version_conflict` with `details.film_id`.
While the same version is still being created you get `409 idempotency_in_progress` with `Retry-After`; while a lower version of the same `video_key` is still being created, `409 previous_version_creating` with `Retry-After`.
A cancelled or deleted film frees its version: the same order then creates a new film.
A setting that cannot be applied returns `400 invalid_request` with `field` and `details.code` (`style_not_available`, `style_not_approved`, `brand_not_available`, `voice_not_available`, `invalid_value`, `unsupported_version`, `unknown_setting`, `ai_music_not_enabled` or `not_applicable`) and `details.setting`.

**Idempotency-Key.** Optional with `video_key`; without `video_key` it is required (`400 idempotency_key_required`).
Errors that happen before mint reserved the order (for example `400` for a template that is not active) are not stored under the key, so you can correct the request and retry with the same key.
A film without `video_key` that was deleted in mint returns `409 film_deleted` for its key.

**Automation.** With `automation` other than `none`, a style must be set (by the template, the previous version or `settings.style_id`), otherwise `400 invalid_request` (`field` `settings.style_id`).
mint estimates the credits from the text length and the automation level; if the company does not have enough credits, the response is `402 insufficient_credits` and nothing is created.
Credits are always charged to the company, never to a team budget. If the automatic production cannot start, the film is still created (`201`) and `notice` tells what to do in mint.
The request body may be at most 256 KB.

Scopes: `films:write`

## Parameters

- `Idempotency-Key` (header): Optional unique key of this write request (1–128 visible ASCII characters). Retries with the same key return the stored response.

## Request body

- `title` (string, required): Title of the film (trimmed).
- `narration_text` (string, required): Text of the film (trimmed, 1–18,000 characters; longer texts return 400 narration_too_long). How it is used depends on settings.narration_mode. Tabs and line breaks are allowed, other control characters are not.
- `user_email` (string (email), required): E-mail address of an active mint user of this company. The user becomes responsible for the film in mint and gets the e-mail when the storyboard is ready. Otherwise 400 user_not_in_company (field user_email). Compared without case.
- `language` (enum): Language of the film. Without it, the template, then the previous version, then de applies. Values: `de`, `en`, `fr`, `es`, `it`, `pt`, `nl`, `pl`, `cs`, `sk`, `hu`, `ro`, `hr`, `sl`, `sv`, `da`, `no`, `fi`, `tr`, `id`, `vi`, `el`, `uk`, `ru`, `bg`.
- `template` (string): Key of an active briefing template (GET /templates). Unknown or inactive keys return 400 template_not_found.
- `automation` (enum): none: mint only creates the film with its briefing; people continue in mint. storyboard (default): mint plans and produces the storyboard automatically and stops at storyboard_ready for review. film: mint also renders the film without stopping at the storyboard (status film_ready). The film is always approved by a person in mint, never by the API. Values: `none`, `storyboard`, `film`.
- `settings` (object): Optional settings. They win over the template and the previous version; unknown keys return 400 invalid_request with field settings.<name>. The template may set further mint settings (for example music, depth or audience) that are not listed here.
  - `format` (enum): Aspect ratio (16x9, 9x16, 1x1; 16:9 etc. are accepted, too). Values: `16x9`, `9x16`, `1x1`, `16:9`, `9:16`, `1:1`.
  - `style_id` (string): Id of an approved style of the company (GET /styles).
  - `brand_id` (nullable string): Id of a brand (GET /brands), or null for no brand. Omit it to keep the brand of the template, the previous version or mint’s automatic choice.
  - `voice_id` (nullable string): Voice id from GET /voices, or null for the automatic voice (the voice of the brand, if it has one).
  - `narration_mode` (enum): verbatim (default): narration_text is spoken word for word. smooth: mint polishes the language of narration_text and keeps its content. free: narration_text is the briefing; mint writes the narration freely (use target_duration_s for the length). Values: `verbatim`, `smooth`, `free`.
  - `tempo` (enum): Pace of the film. Values: `presenting`, `lively`, `kinetic`.
  - `freedom` (enum): Creative freedom in percent. Values: `0`, `25`, `40`, `65`, `100`.
  - `video_share` (enum): Share of moving video clips in percent. Values: `0`, `25`, `50`, `75`, `100`.
  - `target_duration_s` (integer): Target duration in seconds (1–900). mint keeps the length in steps of 6 seconds (0.1 minutes), so the value is rounded to the nearest 6 seconds (at least 6 seconds) and returned that way.
- `category` (object): Category of the film. Allowed forms: { "id" }, { "external_key" }, { "path", "create"? } or { "path", "external_key", "create"? }. With path and external_key, mint first looks for the key; if it is missing and create is true, the path is created and the key is set on its last level. Path and key pointing to different categories return 400 invalid_request (field category). A category that does not exist returns 400 category_not_found. create: true also needs the scope categories:write (otherwise 403 forbidden_scope).
  - `id` (string): Id of a category (GET /categories). Not combinable with other fields.
  - `external_key` (string): External key of a category (e.g. a module number).
  - `path` (array of strings): Category names: [main] or [main, sub] (compared without case).
  - `create` (boolean): Create missing levels of path.
- `video_key` (string): Your stable key of the video (case-sensitive). Together with version it identifies the film within the company.
- `version` (string): Version of the video (major.minor or major.minor.patch, numbers without leading zeros). A higher version of the same video_key takes over style, brand, voice and category of the next lower version, unless set in the request.
- `metadata` (object): Your own data (JSON object, at most 16,384 bytes as UTF-8 JSON, nested at most 8 levels). mint stores and returns it unchanged and never uses it for the film. It is not part of the order identity.

## Returns

- 200: The same order already exists (same video_key, version and content); the existing film is returned and nothing was created.
- 201: The film was created.

## Errors

- 400: Invalid request (invalid_request, invalid_json, invalid_cursor, idempotency_key_required, invalid_idempotency_key, parent_not_found, max_depth_exceeded, narration_too_long, user_not_in_company, template_not_found, category_not_found, feedback_kind_unsupported, upload_not_found).
- 401: Missing, malformed, revoked or expired API key (unauthorized).
- 402: The company does not have enough credits for the requested automation (insufficient_credits). Nothing was created; add credits in mint and retry.
- 403: Not allowed (forbidden_scope, ip_not_allowed, company_inactive, api_not_enabled).
- 404: Not found (not_found).
- 409: Conflict (category_exists, external_key_exists, external_key_conflict, category_limit_reached, idempotency_in_progress, version_conflict, previous_version_creating, film_deleted, not_cancellable, file_unavailable, storyboard_not_ready, storyboard_not_current, timeline_outdated, preview_in_progress, preview_not_ready, preview_unsupported, feedback_queue_full, feedback_not_cancellable, feedback_not_retryable, feedback_busy, revision_locked, film_not_ready, film_busy, no_open_comments, comments_not_open, correction_in_progress, upload_quota_exceeded, film_cancelled).
- 413: Request body too large (payload_too_large).
- 415: Request body is not JSON (unsupported_media_type).
- 422: Idempotency-Key reused with a different request (idempotency_mismatch).
- 429: Too many requests (rate_limited).
- 500: Internal error (internal). Quote the request id when you contact support.
- 503: Temporarily unavailable (service_unavailable). Retry later (after Retry-After seconds, if sent).

## Example request

```sh
curl -X POST "https://api.cream-ai.de/api/v1/films" \
  -H "Authorization: Bearer $MINT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Lesson 3: Market segmentation",
    "narration_text": "Not every customer wants the same thing. In this lesson we look at how companies divide a market into groups …",
    "user_email": "anna.schmidt@example.edu",
    "language": "de",
    "template": "lesson-standard",
    "automation": "storyboard",
    "category": {
      "external_key": "MKT101"
    },
    "video_key": "MKT101-lesson-03",
    "version": "1.0",
    "metadata": {
      "course_id": "MKT101",
      "lesson": 3
    }
  }'
```

## Example response (201)

```json
{
  "object": "film",
  "id": "k7m2xq9v4t8n1b5r3c6d0wza",
  "title": "Lesson 3: Market segmentation",
  "status": "in_production",
  "stage": "planning",
  "attention_required": false,
  "notice": null,
  "source": "api",
  "automation": "storyboard",
  "video_key": "MKT101-lesson-03",
  "version": "1.0",
  "previous_version_id": null,
  "is_latest_approved_version": true,
  "language": "de",
  "template": "lesson-standard",
  "settings": {
    "format": "16x9",
    "style_id": "b6ed57e1-2c4f-4b8a-9d3e-6f1a0c2b7e45",
    "style_revision": 4,
    "brand_id": "brand-northwind-x7k2p9",
    "voice_id": "Xb7hH8MSUJpSbSDYk0k2",
    "voice_name": "Clara",
    "narration_mode": "verbatim",
    "tempo": "presenting",
    "freedom": 40,
    "video_share": 25,
    "target_duration_s": 180
  },
  "category": {
    "object": "category",
    "id": "rz3p8w1d6c0b",
    "name": "MKT101 Marketing basics",
    "external_key": "MKT101",
    "parent": {
      "id": "mk7t2q9x4v1n",
      "name": "Marketing",
      "external_key": "MKT"
    }
  },
  "requested_by": {
    "kind": "user",
    "email": "anna.schmidt@example.edu",
    "name": "Anna Schmidt"
  },
  "metadata": {
    "course_id": "MKT101",
    "lesson": 3
  },
  "approval": null,
  "cancelled_at": null,
  "created_at": "2026-11-10T08:00:00Z",
  "updated_at": "2026-11-10T08:00:04Z",
  "app_url": "https://app.cream-ai.de/editorial/k7m2xq9v4t8n1b5r3c6d0wza",
  "file_url": null,
  "subtitles_url": null,
  "revisions": [],
  "latest_revision": null
}
```
