{
  "openapi": "3.1.0",
  "info": {
    "title": "mint API",
    "version": "1.0.0",
    "description": "The mint API lets your own systems (for example a learning platform) read the catalogs, briefing\ntemplates and categories of your company in mint, create categories and order films: `POST /films` creates a film from a\nbriefing (title, narration text, template or settings); mint can then plan it and produce it automatically up to the\nstoryboard or the finished film. Read your films with `GET /films` and `GET /films/{id}` and cancel them with\n`POST /films/{id}/cancel` until the storyboard is ready. Follow changes in the event feed `GET /events` and download\napproved films with `GET /films/{id}/file` (and their subtitles). It is a server-to-server API: browsers on other sites\ncannot call it (no CORS).\n\n## Films\n\n- Every film is ordered on behalf of a person: `user_email` must belong to an active member of your company. This person\n  is the owner of the film in mint and gets the e-mail \"storyboard ready\", together with the admins of your company.\n- `automation` is `storyboard` (default), `film` or `none`. With automation, mint needs a fixed style (from the template\n  or `settings.style_id`) and enough credits for the estimated film (`402 insufficient_credits` otherwise). The operator of a\n  mint server can switch the automation off; films are then created without it and carry `notice.code`\n  `automation_disabled`.\n- Credits are always taken from the credits of your company, never from a team budget. Each booking of an API film is marked\n  \"API\" with the name of the access in the usage list of your company.\n- `video_key` + `version` identify a film version. The same order again returns the existing film (`200`, nothing is\n  booked); a different order for an existing version returns `409 version_conflict`.\n- Film status: `briefing`, `in_production`, `storyboard_ready`, `film_ready`, `approved` or `cancelled`. Only the approved\n  original version is the film; format and language versions are not part of v1.\n\n## Events and downloads\n\nA person approves a film in mint; only then can it be downloaded. The key needs `films:read`, `events:read` and\n`files:read`. Downloads and the feed are free of credits.\n\n1. `GET /events?limit=1`: note `latest_seq` as H.\n2. `GET /films?status=approved` (all pages): download each film from `file_url` and compare the SHA-256 of the bytes with\n   the `ETag`.\n3. From then on, for example once a day: `GET /events?after=H&types=film.approved,film.approval_revoked,film.deleted`,\n   process the events, store `next_after` as the new H, repeat while `has_more`.\n4. `410 cursor_expired` or `400 invalid_cursor`: start again with step 1.\n\nA film can be downloaded as long as its approval is active: after a revocation the download returns `404 not_approved`;\nafter a new approval only the new revision is available. Films that change between step 1 and 2 come twice; that is\nharmless because the SHA-256 identifies the file.\n\n## Base URL\n\n`https://<your mint host>/api/v1`. All paths in this document are relative to it.\n\n## Authentication\n\nCompany admins create an API access in mint under *Settings → API access*. Each access has its own scopes and keys.\nA key is shown only once, right after it was created. Send it in the `Authorization` header:\n\n```sh\ncurl -H \"Authorization: Bearer mint_sk_live_…\" https://<your mint host>/api/v1/me\n```\n\nKeys in query strings are never accepted. `GET /me` shows the access, its company, scopes, key and limits.\nRevoking a key or changing scopes in mint takes effect immediately.\n\nThe mint API must be enabled for your company by mint. Until then, or after it was disabled, every request with a\nkey of this company returns `403 api_not_enabled`; your accesses and keys are kept and work again once the API is\nenabled.\n\n## Scopes\n\n| Scope | Allows |\n|---|---|\n| `catalog:read` | Read approved styles, brands and voices and active briefing templates of this company as compact lists. |\n| `categories:read` | Read the category tree of this company, also by external key. |\n| `categories:write` | Create and rename categories and subcategories and link them to an external key. Includes `categories:read`. |\n| `films:read` | Read films, status and production progress. |\n| `films:write` | Create films from a briefing, have them produced automatically up to the storyboard or the film, and cancel them. Includes `films:read`. |\n| `files:read` | Download approved film files and subtitles. |\n| `previews:read` | Download the storyboard version of a film as MP4, also before approval. |\n| `previews:write` | Have a storyboard preview of a film created as MP4. Each new preview costs credits. Includes `previews:read`. |\n| `events:read` | Read the continuous event feed of this company with a cursor (film objects in the feed only together with films:read). |\n| `uploads:write` | Upload images and videos to use them in feedback and comments. |\n| `feedback:write` | Send, cancel and retry feedback on scenes, and read and add comments on the finished film. Feedback changes the scenes like a message in the chat and may cost credits. |\n| `films:revise` | Have open comments applied to the film; mint creates a new revision of the film from them (costs credits). Includes `films:read`. |\n\n`GET /me` and `GET /openapi.json` need no scope. More scopes follow with later versions.\n\n## Errors\n\nAll errors share one format:\n\n```json\n{ \"error\": { \"code\": \"forbidden_scope\", \"message\": \"…\", \"request_id\": \"req_…\", \"field\": \"…\", \"details\": {} } }\n```\n\n`code` is stable and machine readable, `message` is for humans and may change, `field` names the affected\ninput. Every response carries an `X-Request-Id` header; please quote it when you contact support.\n\n## Idempotency\n\nWriting `POST` requests require an `Idempotency-Key` header (1–128 visible ASCII characters, for example a UUID);\n`PATCH` accepts it optionally. `POST /films` accepts it optionally when the body has a `video_key` (the version identity\nthen protects against duplicates); without `video_key` it is required (`400 idempotency_key_required`).\n`POST /films/{id}/cancel` accepts it optionally. Repeating a request with the same key and the same body returns the stored response\nwith `Idempotent-Replayed: true` and changes nothing. This also applies to error responses (`400`, `404`,\n`409`): their body is replayed unchanged, so `error.request_id` names the original request, while\n`X-Request-Id` names the current one. Temporary rejections are not stored (`409 feedback_busy`, `409 film_busy`, `409 preview_in_progress`, `409 storyboard_not_ready`, `402 insufficient_credits`, `409 feedback_queue_full`):\na retry with the same key runs again once the reason is gone. The same key with a different request returns\n`422 idempotency_mismatch`; while the first request is still running you get `409 idempotency_in_progress` with\n`Retry-After`. Keys are kept per API access for 24 hours by default; the operator of a mint server can set\n1–72 hours. `GET /me` shows the current value in `idempotency_key_ttl_hours`.\n\n## Pagination\n\nLists return `{ \"object\": \"list\", \"data\": [], \"has_more\": false, \"next_cursor\": null }`. Pass `next_cursor` as\n`cursor` to get the next page; `limit` is 1–100 (default 50). Cursors are opaque and only\nvalid for the same query and filters.\n\n## Limits\n\nRequests are limited per key and per company; `GET /me` shows the limits in `rate_limits`. Exceeding a limit\nreturns `429 rate_limited` with `Retry-After` (seconds). JSON request bodies may be at most\n256 KB and nested at most 32 levels deep (otherwise `400 invalid_request`\nwith `field` `body`). An error before the body was read closes the connection (`Connection: close`). Catalog lists\n(styles, brands, voices) and the category list may show changes with a delay of up to 30 seconds.\n\n## Long-running operations\n\nLong-running requests answer `202 Accepted` with a `Location` header that points to\n`/api/v1/operations/{id}` and an `Operation` object. Poll it until `status` is `succeeded` or `failed`. Operation types\nare `storyboard.preview` and `film.comments_apply`; more may follow within v1.\n\n## Compatibility\n\nWithin v1 there are only additions: new endpoints, new optional request fields, new response fields and new enum\nvalues. Ignore fields you do not know. Breaking changes come with a new version (`/api/v2`)."
  },
  "servers": [
    {
      "url": "/api/v1"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/brands": {
      "get": {
        "operationId": "listBrands",
        "tags": [
          "Catalogs"
        ],
        "summary": "List brands",
        "description": "Brands of the company with their linked styles and voices (no asset URLs). Sorted by `name`, then `id`. Changes made in mint may take up to 30 seconds to appear.",
        "x-mint-scopes": [
          "catalog:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the list.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrandList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/categories": {
      "get": {
        "operationId": "listCategories",
        "tags": [
          "Categories"
        ],
        "summary": "List the category tree",
        "description": "Returns the categories of the company as a tree: top-level categories with their subcategories in `children`. Categories are a bounded two-level tree, so there is no paging (`has_more` is always `false`). With `external_key` only the category with this key is returned (zero or one entry, on any level). With `include=project_count` every category carries `project_count`; this loads all projects of the company and counts as a write request for the rate limits. The list is cached per company for up to 30 seconds, so changes made in mint (and on other servers) can take up to 30 seconds to appear; `project_count` values can be up to 30 seconds old.",
        "x-mint-scopes": [
          "categories:read"
        ],
        "parameters": [
          {
            "name": "external_key",
            "in": "query",
            "required": false,
            "description": "Only the category with this external key (exact match, case-sensitive).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 100,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,99}$"
            }
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "Comma-separated extras. Allowed: `project_count`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The category tree (or the category with the requested external key).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CategoryList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "operationId": "createCategory",
        "tags": [
          "Categories"
        ],
        "summary": "Create a category or subcategory",
        "description": "Creates a top-level category, or a subcategory with `parent_id` (only two levels). With `external_key` the request is safe to repeat: if a category with this key already exists (on any level, even with another `parent_id`), the response is `200` with that category and nothing is changed. The same name on the same level returns `409 category_exists` with `details.category_id`. A company can have at most 500 categories (main and subcategories together); creating one more returns `409 category_limit_reached` with `details.limit`.",
        "x-mint-scopes": [
          "categories:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A category with this external key already exists; it is returned unchanged.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Category"
                }
              }
            }
          },
          "201": {
            "description": "The category was created.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Category"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/categories/{id}": {
      "patch": {
        "operationId": "updateCategory",
        "tags": [
          "Categories"
        ],
        "summary": "Rename a category or change its external key",
        "description": "Changes `name` and/or `external_key` (at least one). `external_key: null` removes the key. Moving a category (`parent_id`) is not supported in v1. A key used by another category returns `409 external_key_exists` with `details.category_id`. If another request changed the same field of this category at the same time, the response is `503 service_unavailable` with `Retry-After`; repeat the request.",
        "x-mint-scopes": [
          "categories:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the category.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CategoryUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated category.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Category"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/events": {
      "get": {
        "operationId": "listEvents",
        "tags": [
          "Events"
        ],
        "summary": "Read the event feed",
        "description": "The event feed of your company: every change of a film ordered through the API that matters to you, in a fixed order. Each event has a `seq` that grows within your company. Read the feed with `after` = `next_after` of the previous page (start with `0`) and store `next_after` only after you processed the page. Duplicates (at least once) can be recognized by `id`. `data` is a snapshot at the time of the event. `film` is the current state of the film (as `GET /films/{id}`, without `revisions`) and is only present if the API key also has the scope `films:read`; it is `null` for a film that was deleted in mint. Types: `film.created`, `film.status_changed` (every change of `status`), `film.storyboard_ready` (once per storyboard), `film.approved`, `film.approval_revoked`, `film.cancelled`, `film.deleted`. A specific event comes before its `film.status_changed`. New types may be added within v1; ignore types you do not know. mint keeps events for 180 days. An `after` older than that returns `410 cursor_expired`; an `after` ahead of the feed (for example from another mint server) returns `400 invalid_cursor`. In both cases start again: note `latest_seq`, read `GET /films?status=approved`, then continue with `after` = the noted `latest_seq`. mint writes events together with the change. In rare cases an event comes up to 10 minutes late; intermediate states can then be missing, the final state is always reported.",
        "x-mint-scopes": [
          "events:read"
        ],
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Return events with a `seq` greater than this (`next_after` of the previous page). Default `0` (from the oldest kept event).",
            "schema": {
              "type": "string",
              "pattern": "^(0|[1-9][0-9]{0,15})$",
              "default": "0"
            }
          },
          {
            "name": "types",
            "in": "query",
            "required": false,
            "description": "Comma-separated list of event types (default: all). Known types: `film.created`, `film.status_changed`, `film.storyboard_ready`, `film.approved`, `film.approval_revoked`, `film.cancelled`, `film.deleted`, `film.progress`, `feedback.updated`, `storyboard.preview_ready`, `operation.completed`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "description": "`api`: films ordered through the API (default and currently the only value).",
            "schema": {
              "type": "string",
              "enum": [
                "api"
              ],
              "default": "api"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size (1–100).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of events.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "410": {
            "description": "This position is no longer kept (`cursor_expired`, `details.min_after`, `details.latest_seq`). Start again with `latest_seq` and `GET /films?status=approved`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films": {
      "post": {
        "operationId": "createFilm",
        "tags": [
          "Films"
        ],
        "summary": "Create a film from a briefing",
        "description": "Creates a film from a briefing in mint and, depending on `automation`, starts the automatic production.\nThe film is always created with the settings of its template (`template`), the previous version and `settings` (in this order, `settings` wins).\n\n**Same order, same film.** With `video_key`, the company-wide identity of a film is `video_key` + `version`.\nSending 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.\n`metadata` is not part of this comparison and is never changed by a repeated order.\nThe same `video_key` and `version` with other content returns `409 version_conflict` with `details.film_id`.\nWhile 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`.\nA cancelled or deleted film frees its version: the same order then creates a new film.\nA 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`.\n\n**Idempotency-Key.** Optional with `video_key`; without `video_key` it is required (`400 idempotency_key_required`).\nErrors 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.\nA film without `video_key` that was deleted in mint returns `409 film_deleted` for its key.\n\n**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`).\nmint 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.\nCredits 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.\nThe request body may be at most 256 KB.",
        "x-mint-scopes": [
          "films:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FilmCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The same order already exists (same `video_key`, `version` and content); the existing film is returned and nothing was created.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Film"
                }
              }
            }
          },
          "201": {
            "description": "The film was created.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Location": {
                "description": "Path of the film: `/api/v1/films/{id}`.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Film"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "get": {
        "operationId": "listFilms",
        "tags": [
          "Films"
        ],
        "summary": "List films",
        "description": "Films of the company, sorted by `created_at` (oldest first), then `id`; new films usually appear at the end. Lists the films of the company that were ordered through the API; every active API access of the company can read them. Projects created in mint are not listed (other sources may be added later). Each film appears at most once while paging. A film created in the same second as a page is read can, rarely, be sorted before the cursor. With `status` mint filters while reading: a page can be shorter than `limit` (even empty) and still have `has_more: true`; keep paging with `next_cursor`. `limit` is 1–100. Title and category in the list can be up to 10 minutes behind mint; `GET /films/{id}` shows the current state. A film deleted in mint disappears from the list.",
        "x-mint-scopes": [
          "films:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only films with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "briefing",
                "in_production",
                "storyboard_ready",
                "film_ready",
                "approved",
                "cancelled"
              ]
            }
          },
          {
            "name": "video_key",
            "in": "query",
            "required": false,
            "description": "Only films with this `video_key` (all versions, exact match, case-sensitive).",
            "schema": {
              "type": "string",
              "maxLength": 200,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,199}$"
            }
          },
          {
            "name": "category_id",
            "in": "query",
            "required": false,
            "description": "Only films in this category; for a top-level category also in its subcategories.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "description": "`api`: films ordered through the API (default and currently the only value; other sources may be added later).",
            "schema": {
              "type": "string",
              "enum": [
                "api"
              ],
              "default": "api"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of films.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilmList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}": {
      "get": {
        "operationId": "getFilm",
        "tags": [
          "Films"
        ],
        "summary": "Get a film",
        "description": "The current state of a film ordered through the API, with its rendered `revisions`. Unknown ids, projects created in mint, films of other companies and films deleted in mint return `404 not_found`.",
        "x-mint-scopes": [
          "films:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The film.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Film"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/cancel": {
      "post": {
        "operationId": "cancelFilm",
        "tags": [
          "Films"
        ],
        "summary": "Cancel a film",
        "description": "Cancels a film ordered through the API, also while mint is planning or producing it automatically, until the storyboard is ready. Running planning and production are stopped; costs that already occurred stay. Once the storyboard (or the film) is ready, the film can no longer be cancelled through the API: `409 not_cancellable` with `details.reason` (for example `not_api_film` for projects created in mint, `other_client` if another API access ordered the film, `storyboard_ready`). Only the API access that ordered the film can cancel it, unless that access was revoked or expired. Cancelling again returns `200` with the cancelled film. A cancelled film frees its `video_key` and `version`: the same order then creates a new film.",
        "x-mint-scopes": [
          "films:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FilmCancel"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The cancelled film.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Film"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/comments": {
      "get": {
        "operationId": "listFilmComments",
        "tags": [
          "Comments"
        ],
        "summary": "List the comments on a film",
        "description": "The comments on a finished film: by default on its latest finished revision, with `revision` on an earlier one. Sorted by `at_s`, then `id`. Comments come from the API and from users in mint; `author` tells who wrote them (personal data). Paging: pass `revision` together with `cursor`; if a newer revision was finished in between, a cursor without `revision` returns `400 invalid_cursor`. `409 film_not_ready` if the film has no finished revision yet; an unknown `revision` returns `404 not_found`.",
        "x-mint-scopes": [
          "feedback:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "revision",
            "in": "query",
            "required": false,
            "description": "Revision of the film (default: the latest finished one).",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of comments (`limit` 1–100).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The film has no finished revision yet (`film_not_ready`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "post": {
        "operationId": "createFilmComment",
        "tags": [
          "Comments"
        ],
        "summary": "Comment on a film",
        "description": "Adds a comment to the latest finished revision of a film, at a time (`at_s`) and optionally a point (`x`, `y`) in the picture. `at_s` is the time in the film **without** the brand intro: if you play the approved file with an intro, subtract the intro length. `message` has 1–2000 characters. `attachments` are up to 5 ids of uploads of your company (`POST /uploads`); each use extends the upload's expiry. Comments are only possible on the latest finished revision: with `revision` set to an older one, or if a newer revision is finished in the meantime, the answer is `409 revision_locked` with `details.latest_revision`. Without a finished film: `409 film_not_ready`. The comment changes nothing by itself; `POST /films/{id}/comments/apply` applies the open comments as a new revision. A film cancelled through the API (`status: cancelled`) takes no more comments: `409 film_cancelled`.",
        "x-mint-scopes": [
          "feedback:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new comment.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Comment"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`revision_locked` (`details.latest_revision`), `film_not_ready` or `film_cancelled` (the film was cancelled through the API).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "An upload in `attachments` has expired (`upload_expired`, `field` names it).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/comments/apply": {
      "post": {
        "operationId": "applyFilmComments",
        "tags": [
          "Comments"
        ],
        "summary": "Apply the comments (new revision)",
        "description": "Applies the open comments of the latest finished revision (all, or the ones in `comment_ids`) as a new revision of the film, like “Apply comments” in mint. Answers `202` with an operation of type `film.comments_apply` (`Location` points to `/api/v1/operations/{id}`). When it has succeeded, `result.id` is the new revision. The same `Idempotency-Key` with the same body always returns the same operation, also after 24 hours; a different body returns `422 idempotency_mismatch`. Errors before the start leave no operation: `409 film_cancelled` (the film was cancelled through the API), `409 film_not_ready` (no finished film or the film is being changed), `409 no_open_comments`, `409 comments_not_open` (`details.comment_ids` are no longer open), `409 correction_in_progress` (a new revision is already being created; `details.operation_id` if it was started through the API), `409 film_busy` (retry after `Retry-After` seconds), `402 insufficient_credits`. Credits are booked like in mint, on the user who ordered the film (`requested_by`). If mint restarts while the comments are being applied, the operation fails with `interrupted_by_restart`; the comments are open again and you can apply them again with a new `Idempotency-Key`. The approval of a film always stays with a person in mint.",
        "x-mint-scopes": [
          "feedback:write",
          "films:revise"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommentsApply"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The operation that creates the new revision (also for a repeated `Idempotency-Key`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              },
              "Location": {
                "description": "Path of the operation (`/api/v1/operations/{id}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Not enough credits for the new revision (`insufficient_credits`). Retry after topping up.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`film_cancelled` (the film was cancelled through the API), `film_not_ready`, `no_open_comments`, `comments_not_open` (`details.comment_ids`), `correction_in_progress` or `film_busy` (with `Retry-After`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/feedback": {
      "post": {
        "operationId": "createFilmFeedback",
        "tags": [
          "Films"
        ],
        "summary": "Send feedback on scenes of the storyboard",
        "description": "Sends feedback on scenes of the storyboard, like a message in the mint chat. mint plans and applies each entry on its own; it never asks back but decides on the most plausible reading. Approving the film stays with a person in mint. Each entry names a scene (`scene`), a time in the film (`at_s`) or both. With `preview_id` the time refers to that storyboard preview; without it, to the current timeline of the film. Several entries for the same scene are applied one after the other in the order of `entries`. The entries are queued (`status: queued`) and handed over as soon as the scene is free. Poll `GET /films/{id}/feedback/{feedback_id}` or listen for `feedback.updated` events. Credits: planning and rendering cost credits according to the mint price list, booked on the company in the name of the user from `user_email` of the order. A redraw of a scene at a fixed price only happens with `allow_redraw: true`. If the company does not have enough credits, the response is `402 insufficient_credits` and nothing is created. Attachments are upload ids of your company (`POST /uploads`). The request is all or nothing: if one entry is invalid, no entry is created (`field` names the entry, e.g. `entries[1].message`). The batch stays bound to its Idempotency-Key: the same key with the same request returns the same entries (also after 24 hours), with another request `422 idempotency_mismatch`. Rejections with a passing reason (`storyboard_not_ready`, `insufficient_credits`) are not stored under the key: a retry with the same key runs again. A film cancelled through the API (`status: cancelled`) takes no more feedback: `409 film_cancelled`. Its feedback can still be read and cancelled.",
        "x-mint-scopes": [
          "feedback:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackCreateRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The entries were queued (or the entries of this Idempotency-Key are returned again).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedbackBatch"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The company does not have enough credits (`insufficient_credits`). Nothing was created; not stored under the Idempotency-Key.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The film was not found, or the preview from `preview_id` was not found (`not_found`, `field: preview_id`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The film was cancelled through the API (`film_cancelled`), no storyboard yet (`storyboard_not_ready`), the timeline no longer matches (`timeline_outdated`, `details.reason`: `preview_from_previous_storyboard` or `scene_boundaries_changed`), too many open entries for this film (`feedback_queue_full`, `details.open`, `details.limit`), or a request with this Idempotency-Key is still running (`idempotency_in_progress`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "An attachment has expired (`upload_expired`, `field` names it). Upload the file again.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "description": "Idempotency-Key reused with a different request (`idempotency_mismatch`, `details.batch_id`), or a scene or time is not in the current storyboard (`scene_not_found`, `field` names the entry).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "get": {
        "operationId": "listFilmFeedback",
        "tags": [
          "Films"
        ],
        "summary": "List the feedback entries of a film",
        "description": "Lists the feedback entries of the film, newest first. Filter with `status`; the filter uses the state of the last `feedback.updated` event, the objects always show the current state.",
        "x-mint-scopes": [
          "feedback:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only entries with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "queued",
                "applying",
                "applied",
                "failed",
                "discarded"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of feedback entries.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedbackList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/feedback/{feedback_id}": {
      "get": {
        "operationId": "getFilmFeedback",
        "tags": [
          "Films"
        ],
        "summary": "Get a feedback entry",
        "description": "Returns one feedback entry with its current state.",
        "x-mint-scopes": [
          "feedback:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "feedback_id",
            "in": "path",
            "required": true,
            "description": "Id of the feedback entry (`id` from the feedback object).",
            "schema": {
              "type": "string",
              "pattern": "^fb_[0-9a-f]{32}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The feedback entry.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Feedback"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/feedback/{feedback_id}/cancel": {
      "post": {
        "operationId": "cancelFilmFeedback",
        "tags": [
          "Films"
        ],
        "summary": "Cancel a feedback entry",
        "description": "Cancels a feedback entry. A queued entry is removed from the queue. An entry being applied is stopped like „Stop“ in the mint chat: the scene gets its previous version back. Answers `200` with the entry (`status: discarded`, `failure.code: cancelled_via_api`). `409 feedback_busy` (with `Retry-After`): the entry is being handed over or the change is being written right now; retry shortly. `409 feedback_not_cancellable`: the entry is already `applied`, `failed` or `discarded`.",
        "x-mint-scopes": [
          "feedback:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "feedback_id",
            "in": "path",
            "required": true,
            "description": "Id of the feedback entry (`id` from the feedback object).",
            "schema": {
              "type": "string",
              "pattern": "^fb_[0-9a-f]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The cancelled entry.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Feedback"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The entry is being handed over or written right now (`feedback_busy`, with `Retry-After`), it can no longer be cancelled (`feedback_not_cancellable`), or a request with this Idempotency-Key is still running (`idempotency_in_progress`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/feedback/{feedback_id}/retry": {
      "post": {
        "operationId": "retryFilmFeedback",
        "tags": [
          "Films"
        ],
        "summary": "Retry a failed feedback entry",
        "description": "Sends a failed feedback entry again (only `status: failed`, otherwise `409 feedback_not_retryable`). The entry is queued again under the calling API access. With `allow_redraw: true` mint may redraw the scene at a fixed price (see `failure.fixed_price_credits` of `redraw_required`). If the company does not have enough credits, the response is `402 insufficient_credits` and the entry stays `failed`. A film cancelled through the API answers `409 film_cancelled`.",
        "x-mint-scopes": [
          "feedback:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "feedback_id",
            "in": "path",
            "required": true,
            "description": "Id of the feedback entry (`id` from the feedback object).",
            "schema": {
              "type": "string",
              "pattern": "^fb_[0-9a-f]{32}$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackRetryRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The entry is queued again (`status: queued`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Feedback"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The company does not have enough credits (`insufficient_credits`). The entry stays `failed`; not stored under the Idempotency-Key.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Only failed entries can be retried (`feedback_not_retryable`), the film was cancelled through the API (`film_cancelled`), the entry changed at the same time (`feedback_busy`, with `Retry-After`), or a request with this Idempotency-Key is still running (`idempotency_in_progress`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/file": {
      "get": {
        "operationId": "downloadFilmFile",
        "tags": [
          "Films"
        ],
        "summary": "Download the approved film file",
        "description": "Downloads the approved file of a film ordered through the API (only the approved original version; format and language versions are not part of v1). The response is the file as `video/mp4` with a strong `ETag` (the SHA-256 of the file, the same as `approval.sha256` of the current approval). Compare it with the SHA-256 of the downloaded bytes. Resume an interrupted download with `Range: bytes=<from>-` and `If-Range: <ETag>`: `206` with the rest, or `200` with the whole file if the approval changed. A single range is supported; several ranges or an invalid one return the whole file. A range behind the end returns `416 range_not_satisfiable`. With `If-None-Match: <ETag>` the response is `304` without a body when the approval is unchanged. `HEAD` returns the headers only. The download always returns the approval that is active now. If an event is older than the current approval, the `ETag` differs from `data.approval.sha256` of the event; then the `ETag` counts. `404 not_approved`: the film has no active approval (never approved or revoked). `409 file_unavailable`: the file is missing or does not match the approval; mint is informed, retry later. `503` with `Retry-After`: mint is checking the file or is briefly unavailable, retry after the given seconds. At most a few downloads per company run at the same time (`429 rate_limited` with `details.reason` `parallel_downloads` and `Retry-After`). Downloads are free of credits.",
        "x-mint-scopes": [
          "files:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films`, `GET /films` or `film_id` of an event).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "description": "One byte range, e.g. `bytes=33554432-`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-Range",
            "in": "header",
            "required": false,
            "description": "The `ETag` of the first response; the range only applies if it still matches.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "required": false,
            "description": "The `ETag` you already have; `304` if unchanged.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The whole approved file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes. Compare it with the SHA-256 of the downloaded bytes.",
                "schema": {
                  "type": "string"
                }
              },
              "Accept-Ranges": {
                "description": "`bytes`: downloads can be resumed with `Range`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Length of the body in bytes.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Content-Disposition": {
                "description": "`attachment; filename=\"<film_id>_r<revision>.<ext>\"`.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store, no-transform`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested part of the approved file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes. Compare it with the SHA-256 of the downloaded bytes.",
                "schema": {
                  "type": "string"
                }
              },
              "Accept-Ranges": {
                "description": "`bytes`: downloads can be resumed with `Range`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Length of the body in bytes.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Content-Disposition": {
                "description": "`attachment; filename=\"<film_id>_r<revision>.<ext>\"`.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store, no-transform`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Range": {
                "description": "`bytes <from>-<to>/<size>`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "Unchanged (the `ETag` from `If-None-Match` still matches).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes. Compare it with the SHA-256 of the downloaded bytes.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Not found (`not_found`) or not approved (`not_approved`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The approved file is not available (`file_unavailable`). mint is informed; retry later.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "416": {
            "description": "The range is behind the end of the file (`range_not_satisfiable`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Content-Range": {
                "description": "`bytes */<size>`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/progress": {
      "get": {
        "operationId": "getFilmProgress",
        "tags": [
          "Films"
        ],
        "summary": "Get the production progress of a film",
        "description": "The production progress of a film ordered through the API. Reading it is free of credits and changes nothing. `status`, `stage` and `attention_required` are the same as in `GET /films/{id}`. Without a production (the film is still in the briefing, or mint has not started yet) `production` is `null`, `steps` is empty and `scenes` and `estimate` are `null`. `steps` follow the production in mint: `audio` (narration), `timeline`, `media` (images and clips), `scenes` (rendering the storyboard scenes) and `film` (rendering the film). A step can have `parts` with their own progress (0–1). `scenes` counts every scene of the current storyboard exactly once: `done` (current and finished), `rendering` (being created or changed right now), `failed` (the last change of this scene failed), `outdated` (finished, but an older state) and `pending` (the rest). `estimate` is a rough guess of the remaining seconds, only while scenes or the film are rendering and once there is enough data; otherwise `null`. Do not use it for deadlines. The response can be up to 5 seconds old. Poll at most every few seconds, or follow the event feed. Unknown ids, projects created in mint, films of other companies and films deleted in mint return `404 not_found`. New values in `steps[].id`, `production.phase` and `production.status` may be added within v1.",
        "x-mint-scopes": [
          "films:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The progress of the film.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilmProgress"
                },
                "examples": {
                  "running": {
                    "summary": "Scenes are rendering",
                    "value": {
                      "object": "film_progress",
                      "film_id": "p8c1d4f0q2w4e6r8t0y2u4i6",
                      "status": "in_production",
                      "stage": "storyboard_in_progress",
                      "attention_required": false,
                      "production": {
                        "status": "running",
                        "phase": "scenes",
                        "storyboard_version": 7,
                        "started_at": "2026-11-04T09:01:12.000Z",
                        "updated_at": "2026-11-04T09:12:40.000Z"
                      },
                      "steps": [
                        {
                          "id": "audio",
                          "status": "done"
                        },
                        {
                          "id": "timeline",
                          "status": "done"
                        },
                        {
                          "id": "media",
                          "status": "done",
                          "parts": [
                            {
                              "id": "bilder",
                              "label": "Bilder erzeugen",
                              "status": "done",
                              "progress": 1,
                              "started_at": "2026-11-04T09:03:00.000Z"
                            }
                          ]
                        },
                        {
                          "id": "scenes",
                          "status": "running",
                          "parts": [
                            {
                              "id": "szenen-rendern",
                              "label": "Szenen rendern",
                              "status": "running",
                              "progress": 0.62,
                              "started_at": "2026-11-04T09:08:02.000Z"
                            }
                          ]
                        },
                        {
                          "id": "film",
                          "status": "waiting"
                        }
                      ],
                      "scenes": {
                        "total": 24,
                        "done": 15,
                        "rendering": 3,
                        "pending": 4,
                        "outdated": 2,
                        "failed": 0
                      },
                      "estimate": {
                        "remaining_s": 260,
                        "confidence": "low",
                        "basis": "scene_rate"
                      }
                    }
                  },
                  "no_production": {
                    "summary": "No production yet",
                    "value": {
                      "object": "film_progress",
                      "film_id": "p8c1d4f0q2w4e6r8t0y2u4i6",
                      "status": "briefing",
                      "stage": "briefing",
                      "attention_required": false,
                      "production": null,
                      "steps": [],
                      "scenes": null,
                      "estimate": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The film was not found (`not_found`): unknown id, a project created in mint, a film of another company or a film deleted in mint.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/storyboard-preview": {
      "post": {
        "operationId": "requestStoryboardPreview",
        "tags": [
          "Films"
        ],
        "summary": "Request a storyboard preview (costs credits)",
        "description": "Has mint create a storyboard preview of the film: one MP4 (1280x720) with the current scene clips in film order, the voice-over and a visible watermark „PREVIEW · Scene n“. It is not the finished film and never replaces the approval in mint. The preview is optional. Each new preview costs credits according to the mint price list (currently 100 credits); mint books them on the company in the name of the user from `user_email` of the order, after the preview is ready. A failed preview is not charged. When requesting, the company must have enough credits for this preview and for all of its previews that are still being created or not charged yet; otherwise the response is `402 insufficient_credits` with `details.required_credits` (the credits needed for all of them) and nothing is created. The response is an operation (`202 Accepted`, `Location: /api/v1/operations/{id}`). Poll it until `status` is `succeeded`; `result.url` then points to the preview. If a ready preview of exactly the same storyboard state exists, the response is `200` with an operation that has already `succeeded` and `reused: true`; it points to that preview and is not charged again. If the same state is being created right now, the response is `202` with `reused: true`. Scenes that are not up to date are shown as „outdated“ or as a dark panel „is still being rendered“. With `require_current: true` the request is rejected with `409 storyboard_not_current` (`details.scenes`) as long as a scene is not up to date. `409 storyboard_not_ready`: the film has no storyboard yet. `409 preview_in_progress` (`details.operation_id`): a preview of another state of this film is still being created; retry later. `409 preview_unsupported`: previews are not available for this kind of film. `409 film_cancelled`: the film was cancelled through the API; existing previews can still be read. `503` with `details.feature` `storyboard_preview`: previews are switched off right now. Rejections with a passing reason (`storyboard_not_ready`, `preview_in_progress`, `insufficient_credits`) are not stored under the Idempotency-Key: a retry with the same key runs again. The operation stays bound to its key: the same key with another request returns `422 idempotency_mismatch`, also after 24 hours.",
        "x-mint-scopes": [
          "films:read",
          "previews:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoryboardPreviewRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A ready preview of the same storyboard state exists (`status` `succeeded`, `reused: true`, not charged again), or the operation of this Idempotency-Key has already finished.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Location": {
                "description": "Path of the operation: `/api/v1/operations/{id}`.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoryboardPreviewOperation"
                }
              }
            }
          },
          "202": {
            "description": "The preview is being created (or the same state is already being created, `reused: true`). Poll the operation from `Location`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Location": {
                "description": "Path of the operation: `/api/v1/operations/{id}`.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoryboardPreviewOperation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "The company does not have enough credits for a preview (`insufficient_credits`, `details.required_credits`). Nothing was created; not stored under the Idempotency-Key.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "No storyboard yet (`storyboard_not_ready`), scenes not up to date with `require_current` (`storyboard_not_current`, `details.scenes`), a preview of another state is being created (`preview_in_progress`, `details.operation_id`), previews not available for this film (`preview_unsupported`), the film was cancelled through the API (`film_cancelled`), or a request with this Idempotency-Key is still running (`idempotency_in_progress`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      },
      "get": {
        "operationId": "getStoryboardPreview",
        "tags": [
          "Films"
        ],
        "summary": "Get a storyboard preview",
        "description": "Returns a storyboard preview of the film: without `preview_id` the newest one, otherwise the one with this id (`result.id` of the operation). `status`: `queued`, `rendering`, `ready`, `failed` or `expired` (previews are kept for a limited time, see `expires_at`). `scenes` shows the time range and state of each scene in the preview (`current`, `stale` = from an earlier version, `missing` = not rendered yet). `current` is `true` if every scene is up to date and the preview belongs to the current storyboard. Download the file with `file_url` as soon as `status` is `ready`.",
        "x-mint-scopes": [
          "films:read",
          "previews:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "preview_id",
            "in": "query",
            "required": false,
            "description": "Id of the preview (`result.id` of the operation). Default: the newest preview of the film.",
            "schema": {
              "type": "string",
              "pattern": "^pv_[a-f0-9]{32}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The storyboard preview.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoryboardPreview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The film or the preview was not found, or the film has no preview yet (`not_found`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/storyboard-preview/file": {
      "get": {
        "operationId": "downloadStoryboardPreview",
        "tags": [
          "Films"
        ],
        "summary": "Download the MP4 of a storyboard preview",
        "description": "Downloads the MP4 of a storyboard preview (without `preview_id` the newest one). The preview is not approved and carries a watermark. The response is `video/mp4` with a strong `ETag` (the SHA-256 of the file in quotes, the same as `sha256` of the preview). Resume an interrupted download with `Range: bytes=<from>-` and `If-Range: <ETag>`. A single range is supported; several or invalid ranges return the whole file; a range behind the end returns `416 range_not_satisfiable`. With `If-None-Match: <ETag>` the response is `304` without a body. `HEAD` returns the headers only. `409 preview_not_ready`: the preview is not ready yet. `410 preview_expired`: the preview has expired; request a new one. `409 file_unavailable`: the file is currently not available; retry later. Downloads are free of credits.",
        "x-mint-scopes": [
          "films:read",
          "previews:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films` or `GET /films`).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "preview_id",
            "in": "query",
            "required": false,
            "description": "Id of the preview. Default: the newest preview of the film.",
            "schema": {
              "type": "string",
              "pattern": "^pv_[a-f0-9]{32}$"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "description": "One byte range, e.g. `bytes=1048576-`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-Range",
            "in": "header",
            "required": false,
            "description": "The `ETag` of the first response; the range only applies if it still matches.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "required": false,
            "description": "The `ETag` you already have; `304` if unchanged.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The whole file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes (the same as `sha256` of the preview).",
                "schema": {
                  "type": "string"
                }
              },
              "Accept-Ranges": {
                "description": "`bytes`: downloads can be resumed with `Range`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Length of the body in bytes.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Content-Disposition": {
                "description": "`attachment; filename=\"storyboard-v<storyboard_version>.mp4\"`.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store, no-transform`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested part of the file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes (the same as `sha256` of the preview).",
                "schema": {
                  "type": "string"
                }
              },
              "Accept-Ranges": {
                "description": "`bytes`: downloads can be resumed with `Range`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Length of the body in bytes.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Content-Disposition": {
                "description": "`attachment; filename=\"storyboard-v<storyboard_version>.mp4\"`.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store, no-transform`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Range": {
                "description": "`bytes <from>-<to>/<size>`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "Unchanged (the `ETag` from `If-None-Match` still matches).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes (the same as `sha256` of the preview).",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The film or the preview was not found, or the film has no preview yet (`not_found`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The preview is not ready yet (`preview_not_ready`) or its file is currently not available (`file_unavailable`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The preview has expired (`preview_expired`). Request a new one.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "416": {
            "description": "The range is behind the end of the file (`range_not_satisfiable`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Content-Range": {
                "description": "`bytes */<size>`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/films/{id}/subtitles.srt": {
      "get": {
        "operationId": "downloadFilmSubtitles",
        "tags": [
          "Films"
        ],
        "summary": "Download the subtitles of the approval",
        "description": "Downloads the subtitles (SubRip) that were frozen with the active approval of a film ordered through the API. Only the subtitles of the approval are returned, never a newer state: `404 subtitles_not_available` if the approval has none (`subtitles_url` of the film is then `null`). ETag, `If-None-Match`, `Range` and `HEAD` work as for the film file.",
        "x-mint-scopes": [
          "files:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the film (`id` from `POST /films`, `GET /films` or `film_id` of an event).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "description": "One byte range.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-Range",
            "in": "header",
            "required": false,
            "description": "The `ETag` of the first response.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "required": false,
            "description": "The `ETag` you already have; `304` if unchanged.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The subtitles (UTF-8).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes. Compare it with the SHA-256 of the downloaded bytes.",
                "schema": {
                  "type": "string"
                }
              },
              "Accept-Ranges": {
                "description": "`bytes`: downloads can be resumed with `Range`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Length of the body in bytes.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Content-Disposition": {
                "description": "`attachment; filename=\"<film_id>_r<revision>.<ext>\"`.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store, no-transform`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/x-subrip": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "206": {
            "description": "The requested part of the subtitles.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes. Compare it with the SHA-256 of the downloaded bytes.",
                "schema": {
                  "type": "string"
                }
              },
              "Accept-Ranges": {
                "description": "`bytes`: downloads can be resumed with `Range`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Length of the body in bytes.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Content-Disposition": {
                "description": "`attachment; filename=\"<film_id>_r<revision>.<ext>\"`.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "`no-store, no-transform`.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Range": {
                "description": "`bytes <from>-<to>/<size>`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/x-subrip": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Unchanged.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "ETag": {
                "description": "Strong ETag: the SHA-256 of the file in quotes. Compare it with the SHA-256 of the downloaded bytes.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Not found (`not_found`), not approved (`not_approved`) or the approval has no subtitles (`subtitles_not_available`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The subtitles are not available (`file_unavailable`). mint is informed; retry later.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "416": {
            "description": "The range is behind the end of the file (`range_not_satisfiable`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Content-Range": {
                "description": "`bytes */<size>`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "summary": "Show the API access of this key",
        "description": "Returns the API access (service account) the key belongs to, its company, scopes, key and limits. Needs a valid key, but no scope.",
        "x-mint-scopes": [],
        "responses": {
          "200": {
            "description": "The API access of this key.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClient"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiDocument",
        "tags": [
          "Account"
        ],
        "summary": "This API description (OpenAPI 3.1)",
        "description": "Returns this document. Needs no key; responses may be cached for 5 minutes.",
        "security": [],
        "x-mint-scopes": [],
        "responses": {
          "200": {
            "description": "The OpenAPI document of the mint API v1.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/operations/{id}": {
      "get": {
        "operationId": "getOperation",
        "tags": [
          "Operations"
        ],
        "summary": "Get an operation",
        "description": "The current state of a long-running operation of your company, for example a storyboard preview (`storyboard.preview`) or applying comments to a finished film (`film.comments_apply`). The `Location` header of the request that started it points here. Poll until `status` is `succeeded` or `failed` (every few seconds is enough); the state is computed when you read it. `result` is set when the operation succeeded: `storyboard_preview` with the URL of the preview, or `film_revision` with the new revision (for example `V3`) and the URL of the film. `error` is set when it failed. Error codes: `start_failed`, `preview_failed`, `correction_failed`, `interrupted_by_restart`. `start_failed`: the operation could not be started; send the request again with a new Idempotency-Key. `interrupted_by_restart`: mint was restarted while applying the comments; they are open again and can be applied again. The scopes depend on the type (`storyboard.preview`: `films:read` + `previews:read`; `film.comments_apply`: `films:read` + `films:revise`; `films:revise` includes `films:read`). A key without them gets `403 forbidden_scope` with `details.required_scopes`. Unknown ids, operations of other companies and operations of films deleted in mint return `404 not_found`, before scopes are checked. New operation types may be added within v1; ignore operations of types you do not know.",
        "x-mint-scopes": [
          "films:read"
        ],
        "x-mint-scopes-by-type": {
          "storyboard.preview": [
            "films:read",
            "previews:read"
          ],
          "film.comments_apply": [
            "films:read",
            "films:revise"
          ]
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the operation (`id` of the `Operation` object, also at the end of the `Location` header).",
            "schema": {
              "type": "string",
              "pattern": "^op_[0-9a-f]{32}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The operation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                },
                "examples": {
                  "preview_succeeded": {
                    "summary": "Storyboard preview is ready",
                    "value": {
                      "object": "operation",
                      "id": "op_71ac0e5b9d2f4c3a8e1b6d7f0a2c4e6b",
                      "type": "storyboard.preview",
                      "status": "succeeded",
                      "created_at": "2026-11-04T09:20:00.000Z",
                      "updated_at": "2026-11-04T09:21:42.000Z",
                      "result": {
                        "object": "storyboard_preview",
                        "id": "pv_3f9a1c7e5b2d4f6a8c0e1b3d5f7a9c2e",
                        "url": "/api/v1/films/p8c1d4f0q2w4e6r8t0y2u4i6/storyboard-preview?preview_id=pv_3f9a1c7e5b2d4f6a8c0e1b3d5f7a9c2e"
                      },
                      "error": null
                    }
                  },
                  "comments_running": {
                    "summary": "Comments are being applied",
                    "value": {
                      "object": "operation",
                      "id": "op_9a12d4e6f8a0b2c4d6e8f0a2b4c6d8e0",
                      "type": "film.comments_apply",
                      "status": "running",
                      "created_at": "2026-11-05T14:02:10.000Z",
                      "updated_at": "2026-11-05T14:02:10.000Z",
                      "result": null,
                      "error": null
                    }
                  },
                  "comments_failed": {
                    "summary": "Interrupted by a restart",
                    "value": {
                      "object": "operation",
                      "id": "op_9a12d4e6f8a0b2c4d6e8f0a2b4c6d8e0",
                      "type": "film.comments_apply",
                      "status": "failed",
                      "created_at": "2026-11-05T14:02:10.000Z",
                      "updated_at": "2026-11-05T14:09:55.000Z",
                      "result": null,
                      "error": {
                        "code": "interrupted_by_restart",
                        "message": "The operation was interrupted by a restart. The comments are open again; you can apply them again."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Not allowed (`forbidden_scope`, `ip_not_allowed`, `company_inactive`, `api_not_enabled`). With `forbidden_scope` the key lacks a scope for this type of operation; `details.required_scopes` lists all scopes the type needs.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The operation was not found (`not_found`): unknown id, an operation of another company or of a film deleted in mint.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/styles": {
      "get": {
        "operationId": "listStyles",
        "tags": [
          "Catalogs"
        ],
        "summary": "List approved styles",
        "description": "Approved editorial styles the company can use for films: its own styles, styles shared with it and global styles, each in its approved revision. Sorted by `updated_at` (newest first), then `id`. Changes made in mint may take up to 30 seconds to appear.",
        "x-mint-scopes": [
          "catalog:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the list.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StyleList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/templates": {
      "get": {
        "operationId": "listTemplates",
        "tags": [
          "Catalogs"
        ],
        "summary": "List briefing templates",
        "description": "Active briefing templates of the company, created by people in mint. Use the `key` as `template` in `POST /films`. `settings` shows only the template values that `POST /films` also accepts in `settings`; the template may set further mint settings (for example music, depth or audience), which apply as well. Sorted by `name` (without case), then `key`. Changes in mint appear immediately.",
        "x-mint-scopes": [
          "catalog:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the list.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/uploads": {
      "post": {
        "operationId": "createUpload",
        "tags": [
          "Uploads"
        ],
        "summary": "Upload a file",
        "description": "Uploads an image or a video for your company, to be used as an attachment of comments (`attachments` of `POST /films/{id}/comments`). Send the bytes of the file as the request body with its media type as `Content-Type` (`image/png`, `image/jpeg`, `image/webp`, `video/mp4`, `video/quicktime`, `video/webm`; no SVG) and a `Content-Length` (required; chunked uploads are rejected with `411 length_required`). Images may be at most 25 MB, videos at most 200 MB. mint checks the content: if it does not match the media type, the answer is `415 file_content_mismatch`. An image of another allowed type (for example a JPEG sent as `image/png`) is stored with its real type; `content_type` of the answer shows it. Uploading the same file (same bytes and type) again returns the existing upload with `200` and extends its expiry; a new upload returns `201`. An upload expires 7 days after it was created or last used. Every API access of your company can use every upload of the company; other companies never see it. The file itself cannot be downloaded through the API. At most 2 uploads run at the same time per mint server: more return `429 rate_limited` with `details.reason` `parallel_uploads` and `Retry-After: 5`. Each company has a storage quota for active uploads (`409 upload_quota_exceeded` with `details.quota_bytes`). A slow upload is aborted after 5 minutes (`503`). Uploads are free of credits.",
        "x-mint-scopes": [
          "uploads:write"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "filename",
            "in": "query",
            "required": false,
            "description": "Name of the file for display (control characters are removed, at most 160 characters). It is never used as a path.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The bytes of the file.",
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary",
                "maxLength": 26214400
              }
            },
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary",
                "maxLength": 26214400
              }
            },
            "image/webp": {
              "schema": {
                "type": "string",
                "format": "binary",
                "maxLength": 26214400
              }
            },
            "video/mp4": {
              "schema": {
                "type": "string",
                "format": "binary",
                "maxLength": 209715200
              }
            },
            "video/quicktime": {
              "schema": {
                "type": "string",
                "format": "binary",
                "maxLength": 209715200
              }
            },
            "video/webm": {
              "schema": {
                "type": "string",
                "format": "binary",
                "maxLength": 209715200
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The same file was uploaded before: the existing upload (expiry extended).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              },
              "Location": {
                "description": "Path of the upload (`/api/v1/uploads/{id}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upload"
                }
              }
            }
          },
          "201": {
            "description": "The new upload.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              },
              "Location": {
                "description": "Path of the upload (`/api/v1/uploads/{id}`).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upload"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The upload storage of your company is full (`upload_quota_exceeded`, `details.quota_bytes`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "`Content-Length` is missing (`length_required`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The file is too large for its type (`payload_too_large`, `details.max_bytes`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Media type not allowed (`unsupported_media_type`) or content does not match it (`file_content_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyMismatch"
          },
          "429": {
            "description": "Too many requests or uploads at the same time (`rate_limited`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/uploads/{id}": {
      "get": {
        "operationId": "getUpload",
        "tags": [
          "Uploads"
        ],
        "summary": "Get an upload",
        "description": "The metadata of an upload of your company (never the file). Uploads of other companies and unknown ids return `404 not_found`; expired uploads `410 upload_expired`.",
        "x-mint-scopes": [
          "uploads:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Id of the upload (`id` from `POST /uploads`).",
            "schema": {
              "type": "string",
              "pattern": "^upl_[0-9a-f]{32}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The upload.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Upload"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "description": "The upload has expired (`upload_expired`). Upload the file again.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/voices": {
      "get": {
        "operationId": "listVoices",
        "tags": [
          "Catalogs"
        ],
        "summary": "List voices",
        "description": "Voices of all brands of the company, one entry per voice and language. Sorted by `name`, then `id`. Changes made in mint may take up to 30 seconds to appear.",
        "x-mint-scopes": [
          "catalog:read"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "description": "Only voices of this language (language code, e.g. `de`).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z]{2,3}$"
            }
          },
          {
            "name": "brand_id",
            "in": "query",
            "required": false,
            "description": "Only voices of this brand (`id` from `GET /brands`).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the list.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "mint_sk_live_<prefix>_<secret>",
        "description": "API key of an API access, created by a company admin in mint. Send it as `Authorization: Bearer <key>`."
      }
    },
    "headers": {
      "RequestId": {
        "description": "Server-generated id of this request. Quote it when you contact support.",
        "schema": {
          "type": "string",
          "pattern": "^req_[0-9a-f]{32}$"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "IdempotentReplayed": {
        "description": "Present with `true` when the response was replayed for a repeated Idempotency-Key (writing requests only, also on replayed 4xx errors).",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Unique key of this write request (1–128 visible ASCII characters, e.g. a UUID). Retries with the same key return the stored response.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[\\x21-\\x7E]{1,128}$"
        }
      },
      "IdempotencyKeyOptional": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional unique key of this write request (1–128 visible ASCII characters). Retries with the same key return the stored response.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[\\x21-\\x7E]{1,128}$"
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque cursor from `next_cursor` of the previous page.",
        "schema": {
          "type": "string",
          "maxLength": 2048
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size (1–100).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "request_id"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "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",
                  "unauthorized",
                  "forbidden_scope",
                  "ip_not_allowed",
                  "insufficient_credits",
                  "company_inactive",
                  "api_not_enabled",
                  "not_found",
                  "not_approved",
                  "subtitles_not_available",
                  "method_not_allowed",
                  "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",
                  "cursor_expired",
                  "preview_expired",
                  "upload_expired",
                  "length_required",
                  "payload_too_large",
                  "unsupported_media_type",
                  "file_content_mismatch",
                  "range_not_satisfiable",
                  "idempotency_mismatch",
                  "scene_not_found",
                  "rate_limited",
                  "internal",
                  "service_unavailable"
                ],
                "description": "Stable, machine-readable error code."
              },
              "message": {
                "type": "string",
                "description": "Human-readable description (English, may change)."
              },
              "request_id": {
                "type": "string",
                "pattern": "^req_[0-9a-f]{32}$"
              },
              "field": {
                "type": "string",
                "description": "Affected input field, if any."
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Additional, code-specific information."
              }
            }
          }
        }
      },
      "Person": {
        "type": "object",
        "required": [
          "kind",
          "name",
          "email"
        ],
        "x-personal-data": true,
        "description": "A person or API access that did something. Personal data: store and process it only as far as you need it.",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "user",
              "api"
            ],
            "description": "`user`: a mint user; `api`: an API access of your company."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Name of the user, or the name of the API access.",
            "x-personal-data": true
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "E-mail address of the user (lower case), if known; always `null` for `api`.",
            "x-personal-data": true
          }
        }
      },
      "List": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {}
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to get the next page; `null` on the last page."
          }
        }
      },
      "Operation": {
        "type": "object",
        "required": [
          "object",
          "id",
          "type",
          "status",
          "created_at",
          "updated_at",
          "result",
          "error"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "operation"
          },
          "id": {
            "type": "string",
            "pattern": "^op_"
          },
          "type": {
            "type": "string",
            "enum": [
              "storyboard.preview",
              "film.comments_apply"
            ],
            "description": "Kind of operation. New types may be added within v1; ignore operations of unknown types."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "failed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Creation time (UTC)."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last change (UTC)."
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "object": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "url": {
                "type": "string"
              }
            }
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "ApiClient": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "organization",
          "scopes",
          "expires_at",
          "key",
          "rate_limits",
          "idempotency_key_ttl_hours",
          "api_version"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "api_client"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "organization": {
            "type": "object",
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Company id in mint."
              },
              "name": {
                "type": "string"
              }
            }
          },
          "scopes": {
            "type": "array",
            "description": "Scopes granted to this access (without implied scopes).",
            "items": {
              "type": "string",
              "enum": [
                "catalog:read",
                "categories:read",
                "categories:write",
                "films:read",
                "films:write",
                "files:read",
                "previews:read",
                "previews:write",
                "events:read",
                "uploads:write",
                "feedback:write",
                "films:revise"
              ]
            }
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Expiry of this API access, set by a company admin in mint. Afterwards every key of the access is rejected with `401`. `null` if the access does not expire."
          },
          "key": {
            "type": "object",
            "required": [
              "id",
              "prefix",
              "expires_at"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "prefix": {
                "type": "string",
                "pattern": "^mint_sk_live_[0-9a-f]{10}$",
                "description": "Visible part of the key."
              },
              "expires_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "End of the grace period of this key after a rotation; `null` if the key itself has no end. The access can still expire earlier, see `expires_at` of the access."
              }
            }
          },
          "rate_limits": {
            "type": "object",
            "required": [
              "read_per_minute",
              "write_per_minute",
              "organization_read_per_minute",
              "organization_write_per_minute"
            ],
            "properties": {
              "read_per_minute": {
                "type": "integer",
                "minimum": 1,
                "description": "Read requests per minute for this key."
              },
              "write_per_minute": {
                "type": "integer",
                "minimum": 1,
                "description": "Write requests per minute for this key."
              },
              "organization_read_per_minute": {
                "type": "integer",
                "minimum": 1,
                "description": "Read requests per minute for all keys of the company."
              },
              "organization_write_per_minute": {
                "type": "integer",
                "minimum": 1,
                "description": "Write requests per minute for all keys of the company."
              }
            }
          },
          "idempotency_key_ttl_hours": {
            "type": "integer",
            "minimum": 1,
            "description": "How long an `Idempotency-Key` of this access is kept, in hours (default 24)."
          },
          "api_version": {
            "type": "string",
            "const": "v1"
          }
        }
      },
      "Style": {
        "type": "object",
        "required": [
          "object",
          "id",
          "slug",
          "name",
          "visibility",
          "access",
          "approved_revision",
          "approved_at",
          "updated_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "style"
          },
          "id": {
            "type": "string",
            "description": "Style id in mint."
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "organization",
              "global"
            ],
            "description": "`organization`: style of a company; `global`: style available to all companies."
          },
          "access": {
            "type": "string",
            "enum": [
              "own",
              "shared",
              "global"
            ],
            "description": "`own`: style of this company; `shared`: style of another company shared with this one; `global`: global style."
          },
          "approved_revision": {
            "type": "integer",
            "minimum": 1,
            "description": "Approved revision of the style."
          },
          "approved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Time of the approval (UTC); `null` if unknown."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last change of the style (UTC)."
          }
        }
      },
      "StyleList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Style"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to get the next page; `null` on the last page."
          }
        }
      },
      "Brand": {
        "type": "object",
        "required": [
          "object",
          "id",
          "slug",
          "name",
          "description",
          "status",
          "style_ids",
          "voices",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "brand"
          },
          "id": {
            "type": "string",
            "description": "Brand id in mint."
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "draft"
            ]
          },
          "style_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Ids of the editorial styles linked to this brand. Use `GET /styles` for the styles that are approved."
          },
          "voices": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "voice_id",
                "name",
                "language",
                "default"
              ],
              "properties": {
                "voice_id": {
                  "type": "string",
                  "description": "Voice id at the voice provider."
                },
                "name": {
                  "type": "string"
                },
                "language": {
                  "type": "string",
                  "description": "Language code, e.g. `de`."
                },
                "default": {
                  "type": "boolean",
                  "description": "Default voice of the brand for this language."
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Creation time (UTC)."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last change (UTC)."
          }
        }
      },
      "BrandList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Brand"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to get the next page; `null` on the last page."
          }
        }
      },
      "Voice": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "language",
          "provider",
          "brands"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "voice"
          },
          "id": {
            "type": "string",
            "description": "Voice id at the voice provider. The same voice appears once per language."
          },
          "name": {
            "type": "string"
          },
          "language": {
            "type": "string",
            "description": "Language code, e.g. `de`."
          },
          "provider": {
            "type": "string",
            "enum": [
              "elevenlabs"
            ]
          },
          "brands": {
            "type": "array",
            "description": "Brands that use this voice for this language, sorted by `brand_id`.",
            "items": {
              "type": "object",
              "required": [
                "brand_id",
                "default"
              ],
              "properties": {
                "brand_id": {
                  "type": "string"
                },
                "default": {
                  "type": "boolean",
                  "description": "Default voice of this brand for this language."
                }
              }
            }
          }
        }
      },
      "VoiceList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Voice"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to get the next page; `null` on the last page."
          }
        }
      },
      "Template": {
        "type": "object",
        "required": [
          "object",
          "id",
          "key",
          "name",
          "language",
          "settings",
          "updated_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "template"
          },
          "id": {
            "type": "string",
            "description": "Id of the template in mint."
          },
          "key": {
            "type": "string",
            "description": "Key of the template, unique in the company. Use it as `template` in `POST /films`."
          },
          "name": {
            "type": "string"
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "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",
              null
            ],
            "description": "Language the template sets, or `null` if it does not set one."
          },
          "settings": {
            "type": "object",
            "description": "Values the template sets, with the names and values of `settings` in `POST /films`. Fields the template does not set are missing.",
            "properties": {
              "format": {
                "type": "string",
                "enum": [
                  "16x9",
                  "9x16",
                  "1x1"
                ],
                "description": "Aspect ratio. Missing if the template does not set it."
              },
              "style_id": {
                "type": "string",
                "description": "Id of the style. Missing if the template does not set it."
              },
              "brand_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Brand, or `null` for no brand. Missing: mint chooses the brand automatically."
              },
              "voice_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Voice, or `null` for the automatic voice. Missing if the template does not set it."
              },
              "narration_mode": {
                "type": "string",
                "enum": [
                  "verbatim",
                  "smooth",
                  "free"
                ],
                "description": "How the text is spoken. Missing if the template does not set it."
              },
              "tempo": {
                "type": "string",
                "enum": [
                  "presenting",
                  "lively",
                  "kinetic"
                ],
                "description": "Pace of the film. Missing if the template does not set it."
              },
              "freedom": {
                "type": "integer",
                "enum": [
                  0,
                  25,
                  40,
                  65,
                  100
                ],
                "description": "Creative freedom in percent. Missing if the template does not set it."
              },
              "video_share": {
                "type": "integer",
                "enum": [
                  0,
                  25,
                  50,
                  75,
                  100
                ],
                "description": "Share of moving video clips in percent. Missing if the template does not set it."
              },
              "target_duration_s": {
                "type": "integer",
                "minimum": 1,
                "description": "Target duration in seconds (in steps of 6 seconds). Templates in mint allow up to 60 minutes; `POST /films` accepts at most 900 seconds in `settings`. Missing if the template does not set it."
              }
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last change of the template (UTC)."
          }
        }
      },
      "TemplateList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Template"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to get the next page; `null` on the last page."
          }
        }
      },
      "Category": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "parent_id",
          "external_key",
          "children"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "category"
          },
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Id of the category in mint."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80,
            "description": "Name, unique (case-insensitive) on its level."
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the top-level category of a subcategory; `null` for top-level categories."
          },
          "external_key": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,99}$",
            "description": "Key of the connected system (e.g. a module number); `null` if not set."
          },
          "project_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Projects assigned directly to this category (not counting subcategories). Only with `include=project_count`."
          },
          "children": {
            "type": "array",
            "description": "Subcategories of a top-level category; always empty for subcategories.",
            "items": {
              "$ref": "#/components/schemas/Category"
            }
          }
        }
      },
      "CategoryList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Category"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "Always `false`: the category tree is returned completely."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Always `null` (no paging)."
          }
        }
      },
      "CategoryCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80,
            "description": "Name of the new category (trimmed; at most 80 characters, longer names are rejected)."
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Id of a top-level category to create a subcategory; omit or `null` for a top-level category."
          },
          "external_key": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,99}$",
            "description": "Optional key of the connected system. Repeating the request with the same key returns the existing category (200)."
          }
        }
      },
      "CategoryUpdate": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80,
            "description": "New name."
          },
          "external_key": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,99}$",
            "description": "New external key; `null` removes it."
          }
        }
      },
      "Film": {
        "type": "object",
        "required": [
          "object",
          "id",
          "title",
          "status",
          "stage",
          "attention_required",
          "notice",
          "source",
          "automation",
          "video_key",
          "version",
          "previous_version_id",
          "is_latest_approved_version",
          "language",
          "template",
          "settings",
          "category",
          "requested_by",
          "metadata",
          "approval",
          "cancelled_at",
          "created_at",
          "updated_at",
          "app_url",
          "file_url",
          "subtitles_url"
        ],
        "description": "A film ordered through the API. `source` is currently always `api`; `app` is reserved for projects created in mint, which v1 does not return yet.",
        "properties": {
          "object": {
            "type": "string",
            "const": "film"
          },
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$",
            "description": "Id of the film in mint."
          },
          "title": {
            "type": "string",
            "description": "Title as it is in mint now (people can change it there)."
          },
          "status": {
            "type": "string",
            "enum": [
              "briefing",
              "in_production",
              "storyboard_ready",
              "film_ready",
              "approved",
              "cancelled"
            ],
            "description": "`briefing`: created in mint, nothing is running (for example with `automation: none`). `in_production`: mint is planning or producing the storyboard (or the film). `storyboard_ready`: the storyboard is ready for review in mint. With `automation: storyboard` the film stops here until a person continues in mint. `film_ready`: the film is rendered and waits for approval by a person in mint. `approved`: a person approved the film in mint. `cancelled`: the film was cancelled. New values may be added within v1."
          },
          "stage": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "briefing",
              "style",
              "voice",
              "ready_to_plan",
              "planning",
              "storyboard_in_progress",
              "storyboard_review",
              "film_rendering",
              "film_ready",
              "approved",
              "paused",
              "error",
              "cancelled",
              null
            ],
            "description": "Detailed stage as shown in the mint project list, or `null` if unknown. New values may be added within v1."
          },
          "attention_required": {
            "type": "boolean",
            "description": "`true` if a person needs to act in mint (see `notice`)."
          },
          "notice": {
            "type": [
              "object",
              "null"
            ],
            "required": [
              "code",
              "message"
            ],
            "description": "Why a person needs to act in mint, or `null`. Codes: `production_paused` (“Production is paused in mint. Someone needs to resume it.”), `production_failed` (“Production failed in mint. Someone needs to check it.”), `planning_failed` (“Planning the storyboard failed in mint. Someone needs to check it.”), `automation_failed` (“The automatic storyboard could not be completed. Someone needs to continue in mint.”), `insufficient_credits` (“The company does not have enough credits. Someone needs to add credits and continue in mint.”), `automation_disabled` (“Automatic production is currently switched off. Someone needs to continue in mint.”).",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "production_paused",
                  "production_failed",
                  "planning_failed",
                  "automation_failed",
                  "insufficient_credits",
                  "automation_disabled"
                ]
              },
              "message": {
                "type": "string",
                "description": "Short English text (may change)."
              }
            }
          },
          "source": {
            "type": "string",
            "enum": [
              "api",
              "app"
            ],
            "description": "`api`: ordered through the API; `app`: reserved for projects created in mint (not returned in v1)."
          },
          "automation": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "none",
              "storyboard",
              "film",
              null
            ],
            "description": "`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."
          },
          "video_key": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your key of the video, as sent."
          },
          "version": {
            "type": [
              "string",
              "null"
            ],
            "description": "Version of the video, canonical (`1.0.0` is returned as `1.0`)."
          },
          "previous_version_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the film with the next lower version of the same `video_key`, or `null`."
          },
          "is_latest_approved_version": {
            "type": "boolean",
            "description": "`true` only if this film is approved and no higher, approved and not cancelled version of the same `video_key` exists."
          },
          "language": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "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",
              null
            ],
            "description": "Language of the film."
          },
          "template": {
            "type": [
              "string",
              "null"
            ],
            "description": "Key of the template used when the film was created."
          },
          "settings": {
            "$ref": "#/components/schemas/FilmSettings"
          },
          "category": {
            "type": [
              "object",
              "null"
            ],
            "required": [
              "id",
              "name",
              "external_key",
              "parent"
            ],
            "description": "Category of the film in mint (see `GET /categories`), or `null`.",
            "properties": {
              "object": {
                "type": "string",
                "const": "category"
              },
              "id": {
                "type": "string",
                "pattern": "^[A-Za-z0-9_-]{1,64}$",
                "description": "Id of the category."
              },
              "name": {
                "type": "string"
              },
              "external_key": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Key of the connected system, if set."
              },
              "parent": {
                "type": [
                  "object",
                  "null"
                ],
                "required": [
                  "id",
                  "name",
                  "external_key"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{1,64}$"
                  },
                  "name": {
                    "type": "string"
                  },
                  "external_key": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "description": "Top-level category of a subcategory; `null` for a top-level category."
              }
            }
          },
          "requested_by": {
            "type": [
              "object",
              "null"
            ],
            "required": [
              "kind",
              "email",
              "name"
            ],
            "x-personal-data": true,
            "description": "The mint user from `user_email` of the request (a `Person` with `kind` `user`). This user is responsible for the film in mint and is notified when the storyboard is ready. Personal data.",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "user"
                ],
                "description": "Always `user`."
              },
              "email": {
                "type": "string",
                "description": "E-mail address (lower case).",
                "x-personal-data": true
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Name of the user at the time the film was created.",
                "x-personal-data": true
              }
            }
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Your metadata, as sent. mint never uses it for the film. Key order is kept in `POST /films` (first response), `GET /films` and `GET /films/{id}`; a replayed response may order keys differently."
          },
          "approval": {
            "type": [
              "object",
              "null"
            ],
            "required": [
              "revision",
              "approved_at",
              "sha256",
              "bytes",
              "duration_s",
              "resolution",
              "mime",
              "has_subtitles",
              "approved_by"
            ],
            "description": "The active approval of the film (only the approved original version is the film), or `null`.",
            "properties": {
              "revision": {
                "type": "integer",
                "minimum": 1,
                "description": "Approved revision of the film."
              },
              "approved_at": {
                "type": "string",
                "format": "date-time",
                "description": "Time of the approval (UTC)."
              },
              "sha256": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "SHA-256 of the approved file (hex)."
              },
              "bytes": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Size of the approved file in bytes."
              },
              "duration_s": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": 0,
                "description": "Duration in seconds."
              },
              "resolution": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Resolution, e.g. `1920x1080`."
              },
              "mime": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Media type of the file, e.g. `video/mp4`."
              },
              "has_subtitles": {
                "type": "boolean",
                "description": "`true` if subtitles were frozen with this approval (`subtitles_url`)."
              },
              "approved_by": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/Person"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Who approved the film in mint (`kind` `user`, name and e-mail; `email` is `null` for approvals before 9 November 2026). Personal data."
              }
            }
          },
          "cancelled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Time of the cancellation (UTC), or `null`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Creation time (UTC)."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last change (UTC)."
          },
          "app_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link to the film in mint (for people with access), or `null`."
          },
          "file_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Path of the approved file (`GET /films/{id}/file`, relative to the host of the API), or `null` without an active approval."
          },
          "subtitles_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Path of the subtitles of the approval (`GET /films/{id}/subtitles.srt`), or `null` if the approval has none."
          },
          "revisions": {
            "type": "array",
            "description": "Rendered revisions of the film, newest last (only in `GET /films/{id}`, without file links).",
            "items": {
              "type": "object",
              "required": [
                "revision",
                "status",
                "created_at",
                "finished_at",
                "duration_s",
                "resolution"
              ],
              "properties": {
                "revision": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "Number of the rendered revision."
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "rendering",
                    "ready",
                    "failed"
                  ],
                  "description": "`rendering`, `ready` or `failed`."
                },
                "created_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time",
                  "description": "Start of the render (UTC)."
                },
                "finished_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time",
                  "description": "End of the render (UTC), `null` while rendering."
                },
                "duration_s": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "minimum": 0,
                  "description": "Duration in seconds."
                },
                "resolution": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Resolution, e.g. `1920x1080`."
                }
              }
            }
          }
        }
      },
      "FilmSettings": {
        "type": "object",
        "required": [
          "format",
          "style_id",
          "style_revision",
          "brand_id",
          "voice_id",
          "voice_name",
          "narration_mode",
          "tempo",
          "freedom",
          "video_share",
          "target_duration_s"
        ],
        "description": "Effective settings of the film in mint. Values that are not set (or that mint cannot express in the API) are `null`.",
        "properties": {
          "format": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "16x9",
              "9x16",
              "1x1",
              null
            ],
            "description": "Aspect ratio."
          },
          "style_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the style (see `GET /styles`)."
          },
          "style_revision": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Revision of the style the film uses."
          },
          "brand_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Effective brand (see `GET /brands`); `null` = no brand."
          },
          "voice_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Voice of the narration (may also be a voice from the mint voice library); `null` = none set yet."
          },
          "voice_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Name of the voice."
          },
          "narration_mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "verbatim",
              "smooth",
              "free",
              null
            ],
            "description": "`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)."
          },
          "tempo": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "presenting",
              "lively",
              "kinetic",
              null
            ],
            "description": "Pace of the film."
          },
          "freedom": {
            "type": [
              "integer",
              "null"
            ],
            "enum": [
              0,
              25,
              40,
              65,
              100,
              null
            ],
            "description": "Creative freedom in percent."
          },
          "video_share": {
            "type": [
              "integer",
              "null"
            ],
            "enum": [
              0,
              25,
              50,
              75,
              100,
              null
            ],
            "description": "Share of moving video clips in percent."
          },
          "target_duration_s": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Target duration in seconds (in steps of 6 seconds)."
          }
        }
      },
      "FilmCreate": {
        "type": "object",
        "required": [
          "title",
          "narration_text",
          "user_email"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Title of the film (trimmed)."
          },
          "narration_text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 18000,
            "description": "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": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "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": {
            "type": "string",
            "enum": [
              "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"
            ],
            "description": "Language of the film. Without it, the template, then the previous version, then `de` applies."
          },
          "template": {
            "type": "string",
            "pattern": "^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$",
            "description": "Key of an active briefing template (`GET /templates`). Unknown or inactive keys return `400 template_not_found`."
          },
          "automation": {
            "type": "string",
            "enum": [
              "none",
              "storyboard",
              "film"
            ],
            "description": "`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.",
            "default": "storyboard"
          },
          "settings": {
            "$ref": "#/components/schemas/FilmCreateSettings"
          },
          "category": {
            "$ref": "#/components/schemas/FilmCreateCategory"
          },
          "video_key": {
            "type": "string",
            "maxLength": 200,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,199}$",
            "description": "Your stable key of the video (case-sensitive). Together with `version` it identifies the film within the company."
          },
          "version": {
            "type": "string",
            "pattern": "^(0|[1-9]\\d{0,5})(\\.(0|[1-9]\\d{0,5})){1,2}$",
            "default": "1.0",
            "description": "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": {
            "type": "object",
            "additionalProperties": true,
            "description": "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."
          }
        }
      },
      "FilmCreateSettings": {
        "type": "object",
        "additionalProperties": false,
        "description": "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.",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "16x9",
              "9x16",
              "1x1",
              "16:9",
              "9:16",
              "1:1"
            ],
            "description": "Aspect ratio (`16x9`, `9x16`, `1x1`; `16:9` etc. are accepted, too)."
          },
          "style_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120,
            "description": "Id of an approved style of the company (`GET /styles`)."
          },
          "brand_id": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 120,
            "description": "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": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[A-Za-z0-9_-]{4,80}$",
            "description": "Voice id from `GET /voices`, or `null` for the automatic voice (the voice of the brand, if it has one)."
          },
          "narration_mode": {
            "type": "string",
            "enum": [
              "verbatim",
              "smooth",
              "free"
            ],
            "default": "verbatim",
            "description": "`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)."
          },
          "tempo": {
            "type": "string",
            "enum": [
              "presenting",
              "lively",
              "kinetic"
            ],
            "description": "Pace of the film."
          },
          "freedom": {
            "type": "integer",
            "enum": [
              0,
              25,
              40,
              65,
              100
            ],
            "description": "Creative freedom in percent."
          },
          "video_share": {
            "type": "integer",
            "enum": [
              0,
              25,
              50,
              75,
              100
            ],
            "description": "Share of moving video clips in percent."
          },
          "target_duration_s": {
            "type": "integer",
            "minimum": 1,
            "maximum": 900,
            "description": "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."
          }
        }
      },
      "FilmCreateCategory": {
        "type": "object",
        "additionalProperties": false,
        "description": "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`).",
        "anyOf": [
          {
            "required": [
              "id"
            ]
          },
          {
            "required": [
              "external_key"
            ]
          },
          {
            "required": [
              "path"
            ]
          }
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Id of a category (`GET /categories`). Not combinable with other fields."
          },
          "external_key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,99}$",
            "description": "External key of a category (e.g. a module number)."
          },
          "path": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80
            },
            "description": "Category names: `[main]` or `[main, sub]` (compared without case)."
          },
          "create": {
            "type": "boolean",
            "default": false,
            "description": "Create missing levels of `path`."
          }
        }
      },
      "FilmList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Film"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "`true` if more films may follow (also after a short or empty page when filtering by `status`)."
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to get the next page; `null` on the last page."
          }
        }
      },
      "FilmCancel": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "Optional reason (at most 500 characters) for the cancellation."
          }
        }
      },
      "Event": {
        "type": "object",
        "required": [
          "object",
          "id",
          "seq",
          "type",
          "occurred_at",
          "film_id",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "event"
          },
          "id": {
            "type": "string",
            "pattern": "^evt_[0-9]+$",
            "description": "Stable id of the event (`evt_<seq>`)."
          },
          "seq": {
            "type": "integer",
            "minimum": 1,
            "description": "Position in the feed; grows within your company (with gaps)."
          },
          "type": {
            "type": "string",
            "description": "Type of the event. Known types: `film.created`, `film.status_changed`, `film.storyboard_ready`, `film.approved`, `film.approval_revoked`, `film.cancelled`, `film.deleted`, `film.progress`, `feedback.updated`, `storyboard.preview_ready`, `operation.completed`. New types may be added."
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "Time of the change (UTC)."
          },
          "film_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the film."
          },
          "data": {
            "type": "object",
            "additionalProperties": true,
            "description": "Snapshot at the time of the event: `status` (after the event), `previous_status`, `approval`, and the person who triggered the event (`requested_by`, `cancelled_by`, `approval.approved_by`, `approval.revoked_by`; personal data).",
            "properties": {
              "status": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "briefing",
                  "in_production",
                  "storyboard_ready",
                  "film_ready",
                  "approved",
                  "cancelled",
                  null
                ],
                "description": "Status of the film after the event."
              },
              "previous_status": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Status before the event (`null` if the feed did not know the film yet)."
              },
              "approval": {
                "type": "object",
                "required": [
                  "id",
                  "revision"
                ],
                "description": "Snapshot of the approval at the time of the event (in `film.approved` complete, in `film.approval_revoked` only `id` and `revision`).",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Id of the approval."
                  },
                  "revision": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1,
                    "description": "Approved revision of the film."
                  },
                  "approved_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "Time of the approval (UTC)."
                  },
                  "sha256": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "SHA-256 of the approved file (hex)."
                  },
                  "bytes": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Size of the approved file in bytes."
                  },
                  "duration_s": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Duration in seconds."
                  },
                  "resolution": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Resolution, e.g. `1920x1080`."
                  },
                  "mime": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Media type of the file."
                  },
                  "has_subtitles": {
                    "type": "boolean",
                    "description": "`true` if subtitles were frozen with this approval."
                  },
                  "approved_by": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/Person"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "`film.approved`: who approved the film in mint. Personal data."
                  },
                  "revoked_by": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/Person"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "`film.approval_revoked`: who revoked the approval in mint. Personal data."
                  }
                }
              },
              "requested_by": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/Person"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "`film.created`: the mint user from `user_email` of the order. Personal data."
              },
              "cancelled_by": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/Person"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "`film.cancelled`: the API access that cancelled the film (`kind` `api`). Personal data."
              }
            }
          },
          "film": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Film"
              },
              {
                "type": "null"
              }
            ],
            "description": "The current film (as `GET /films/{id}`, without `revisions`); `null` if it was deleted. Only present with the scope `films:read`."
          }
        }
      },
      "EventList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_after",
          "latest_seq"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "has_more": {
            "type": "boolean",
            "description": "`true` if more events follow right away."
          },
          "next_after": {
            "type": "integer",
            "minimum": 0,
            "description": "Pass as `after` to continue. Store it only after you processed the page."
          },
          "latest_seq": {
            "type": "integer",
            "minimum": 0,
            "description": "The newest position of the feed of your company (for a restart, see above)."
          }
        }
      },
      "FilmProgress": {
        "type": "object",
        "required": [
          "object",
          "film_id",
          "status",
          "stage",
          "attention_required",
          "production",
          "steps",
          "scenes",
          "estimate"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "film_progress"
          },
          "film_id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$",
            "description": "Id of the film."
          },
          "status": {
            "type": "string",
            "enum": [
              "briefing",
              "in_production",
              "storyboard_ready",
              "film_ready",
              "approved",
              "cancelled"
            ],
            "description": "Status of the film, as in `GET /films/{id}`."
          },
          "stage": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "briefing",
              "style",
              "voice",
              "ready_to_plan",
              "planning",
              "storyboard_in_progress",
              "storyboard_review",
              "film_rendering",
              "film_ready",
              "approved",
              "paused",
              "error",
              "cancelled",
              null
            ],
            "description": "Detailed stage, as in `GET /films/{id}`, or `null` if unknown. New values may be added within v1."
          },
          "attention_required": {
            "type": "boolean",
            "description": "`true` if a person needs to act in mint, as in `GET /films/{id}`."
          },
          "production": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FilmProgressProduction"
              },
              {
                "type": "null"
              }
            ],
            "description": "The latest production of the film, or `null` if there is none yet."
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilmProgressStep"
            },
            "description": "Steps of the production in order; empty without a production."
          },
          "scenes": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FilmProgressScenes"
              },
              {
                "type": "null"
              }
            ],
            "description": "Scene counts of the current storyboard, or `null` without a production."
          },
          "estimate": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FilmProgressEstimate"
              },
              {
                "type": "null"
              }
            ],
            "description": "Rough estimate of the remaining time, or `null`."
          }
        }
      },
      "FilmProgressProduction": {
        "type": "object",
        "required": [
          "status",
          "phase",
          "storyboard_version",
          "started_at",
          "updated_at"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "storyboard_ready",
              "film_rendering",
              "film_ready",
              "failed",
              "cancelled"
            ],
            "description": "`queued`, `running`, `storyboard_ready` (the storyboard is ready, no film is rendering), `film_rendering`, `film_ready`, `failed` or `cancelled`. New values may be added within v1."
          },
          "phase": {
            "type": "string",
            "enum": [
              "audio",
              "timeline",
              "media",
              "scenes",
              "film"
            ],
            "description": "Current step (see `steps[].id`)."
          },
          "storyboard_version": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Version of the storyboard this production works on."
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Start of the production (UTC)."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Last change of the production (UTC)."
          }
        }
      },
      "FilmProgressStep": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "audio",
              "timeline",
              "media",
              "scenes",
              "film"
            ],
            "description": "`audio`, `timeline`, `media`, `scenes` or `film`. New values may be added within v1."
          },
          "status": {
            "type": "string",
            "enum": [
              "waiting",
              "running",
              "done",
              "failed"
            ]
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilmProgressPart"
            },
            "description": "Parts of the step with their own progress (only present if the step has parts)."
          }
        }
      },
      "FilmProgressPart": {
        "type": "object",
        "required": [
          "id",
          "label",
          "status",
          "progress",
          "started_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$",
            "description": "Stable technical id of the part."
          },
          "label": {
            "type": "string",
            "description": "Display text of the part as shown in mint (German)."
          },
          "status": {
            "type": "string",
            "enum": [
              "waiting",
              "running",
              "done",
              "failed"
            ]
          },
          "progress": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Progress from 0 to 1, or `null` if unknown."
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Start of the part (UTC), or `null`."
          }
        }
      },
      "FilmProgressScenes": {
        "type": "object",
        "required": [
          "total",
          "done",
          "rendering",
          "pending",
          "outdated",
          "failed"
        ],
        "properties": {
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Scenes of the current storyboard (sum of the other counts)."
          },
          "done": {
            "type": "integer",
            "minimum": 0,
            "description": "Finished in the current state."
          },
          "rendering": {
            "type": "integer",
            "minimum": 0,
            "description": "Being created or changed right now."
          },
          "pending": {
            "type": "integer",
            "minimum": 0,
            "description": "Not started yet."
          },
          "outdated": {
            "type": "integer",
            "minimum": 0,
            "description": "Finished, but in an older state of the storyboard."
          },
          "failed": {
            "type": "integer",
            "minimum": 0,
            "description": "The last change of the scene failed."
          }
        }
      },
      "FilmProgressEstimate": {
        "type": "object",
        "required": [
          "remaining_s",
          "confidence",
          "basis"
        ],
        "properties": {
          "remaining_s": {
            "type": "integer",
            "minimum": 0,
            "description": "Estimated remaining seconds."
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium"
            ],
            "description": "`low` or `medium`. Treat it as a rough guess."
          },
          "basis": {
            "type": "string",
            "enum": [
              "scene_rate",
              "film_render"
            ],
            "description": "`scene_rate`: from the scenes finished so far; `film_render`: from the progress of the film render. New values may be added within v1."
          }
        }
      },
      "StoryboardPreviewRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "require_current": {
            "type": "boolean",
            "default": false,
            "description": "`true`: only create the preview if every scene is up to date (otherwise `409 storyboard_not_current`). Default `false`: outdated and missing scenes are marked in the preview."
          }
        }
      },
      "StoryboardPreviewOperation": {
        "description": "Operation of type `storyboard.preview`. `result` (when `succeeded`) is `{ object: \"storyboard_preview\", id, url }`.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Operation"
          },
          {
            "type": "object",
            "required": [
              "reused"
            ],
            "properties": {
              "reused": {
                "type": "boolean",
                "description": "`true` if the request reuses a preview of the same storyboard state (ready or being created); it is not charged again."
              }
            }
          }
        ]
      },
      "StoryboardPreviewScene": {
        "type": "object",
        "required": [
          "nr",
          "start_s",
          "end_s",
          "state"
        ],
        "properties": {
          "nr": {
            "type": "integer",
            "minimum": 1,
            "description": "Number of the scene."
          },
          "start_s": {
            "type": "number",
            "minimum": 0,
            "description": "Start of the scene in the preview (seconds)."
          },
          "end_s": {
            "type": "number",
            "minimum": 0,
            "description": "End of the scene in the preview (seconds)."
          },
          "state": {
            "type": "string",
            "enum": [
              "current",
              "stale",
              "missing"
            ],
            "description": "`current`: clip of the current version; `stale`: clip of an earlier version (marked „outdated“); `missing`: no clip yet (dark panel)."
          }
        }
      },
      "StoryboardPreview": {
        "type": "object",
        "required": [
          "object",
          "id",
          "film_id",
          "status",
          "storyboard_version",
          "current",
          "duration_s",
          "fps",
          "resolution",
          "bytes",
          "sha256",
          "watermark",
          "scenes",
          "file_url",
          "created_at",
          "ready_at",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "storyboard_preview"
          },
          "id": {
            "type": "string",
            "pattern": "^pv_[a-f0-9]{32}$",
            "description": "Id of the preview."
          },
          "film_id": {
            "type": "string",
            "description": "Id of the film."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "rendering",
              "ready",
              "failed",
              "expired"
            ]
          },
          "storyboard_version": {
            "type": "integer",
            "minimum": 0,
            "description": "Version of the storyboard the preview shows."
          },
          "current": {
            "type": "boolean",
            "description": "`true` if every scene is `current` and the preview belongs to the current storyboard."
          },
          "duration_s": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Duration in seconds (planned until `ready`)."
          },
          "fps": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Frames per second."
          },
          "resolution": {
            "type": [
              "string",
              "null"
            ],
            "description": "Resolution, e.g. `1280x720`."
          },
          "bytes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Size of the file in bytes (when `ready`)."
          },
          "sha256": {
            "type": [
              "string",
              "null"
            ],
            "description": "SHA-256 of the file (hex, when `ready`); the same as the `ETag` of the download."
          },
          "watermark": {
            "type": "boolean",
            "description": "`true`: the preview carries a visible watermark."
          },
          "scenes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StoryboardPreviewScene"
            }
          },
          "file_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Path of the file download (when `ready`)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Time of the request (UTC)."
          },
          "ready_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Time the preview became ready (UTC)."
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Time the preview expires (UTC); afterwards the file is deleted."
          }
        }
      },
      "FeedbackRegion": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "x",
          "y",
          "w",
          "h"
        ],
        "description": "Area of the frame as fractions of width and height (0..1, origin top left). It is cut to the frame.",
        "properties": {
          "x": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "y": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "w": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Width; the area must be greater than 0."
          },
          "h": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Height; the area must be greater than 0."
          }
        }
      },
      "FeedbackPoint": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "x",
          "y"
        ],
        "description": "Point in the frame as fractions of width and height (0..1, origin top left).",
        "properties": {
          "x": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "y": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "FeedbackEntryRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "message"
        ],
        "anyOf": [
          {
            "required": [
              "scene"
            ]
          },
          {
            "required": [
              "at_s"
            ]
          }
        ],
        "description": "One feedback entry. It needs `scene`, `at_s` or both; with both, `at_s` must lie within the scene.",
        "properties": {
          "scene": {
            "type": "integer",
            "minimum": 1,
            "description": "Number of the scene in the current storyboard."
          },
          "at_s": {
            "type": "number",
            "minimum": 0,
            "description": "Time in the film (or in the preview from `preview_id`) in seconds; it selects the scene at that time."
          },
          "kind": {
            "type": "string",
            "enum": [
              "text",
              "image",
              "layout",
              "general"
            ],
            "default": "general",
            "description": "What the feedback is about. `timing` and `voice` are not supported (`400 feedback_kind_unsupported`)."
          },
          "message": {
            "type": "string",
            "minLength": 3,
            "maxLength": 20000,
            "description": "The feedback in your words (3 to 20000 characters after trimming; at most 100000 characters per request)."
          },
          "region": {
            "$ref": "#/components/schemas/FeedbackRegion"
          },
          "point": {
            "$ref": "#/components/schemas/FeedbackPoint"
          },
          "attachments": {
            "type": "array",
            "maxItems": 4,
            "description": "Upload ids of your company (at most 4 per entry, 20 different per request). Unknown or foreign uploads: `400 upload_not_found`; expired: `410 upload_expired`.",
            "items": {
              "type": "string",
              "pattern": "^upl_[0-9a-f]{32}$"
            }
          },
          "allow_redraw": {
            "type": "boolean",
            "default": false,
            "description": "`true`: mint may redraw the scene at a fixed price if the change needs it. Otherwise such an entry fails with `redraw_required`."
          },
          "external_ref": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Your own reference (e.g. a ticket id); returned unchanged and in `feedback.updated` events."
          }
        }
      },
      "FeedbackCreateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "entries"
        ],
        "properties": {
          "preview_id": {
            "type": "string",
            "pattern": "^pv_[a-f0-9]{32}$",
            "description": "Storyboard preview the times (`at_s`) refer to. It must belong to the current storyboard of the film."
          },
          "entries": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/FeedbackEntryRequest"
            }
          }
        }
      },
      "FeedbackRetryRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "allow_redraw": {
            "type": "boolean",
            "description": "Change the permission to redraw the scene; without it the previous value applies."
          }
        }
      },
      "FeedbackAttachment": {
        "type": "object",
        "required": [
          "upload_id",
          "media_id"
        ],
        "properties": {
          "upload_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the upload."
          },
          "media_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the file in the media of the film."
          }
        }
      },
      "FeedbackResult": {
        "type": "object",
        "required": [
          "summary",
          "action",
          "storyboard_version",
          "render"
        ],
        "properties": {
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "What mint changed (German, as in the mint chat)."
          },
          "action": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "text",
              "image",
              "layout",
              "redraw",
              "regenerate",
              "none",
              null
            ],
            "description": "Kind of change."
          },
          "storyboard_version": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Storyboard version with the change."
          },
          "render": {
            "type": "string",
            "enum": [
              "pending",
              "done"
            ],
            "description": "`pending`: the scene is not rendered yet; `done`: rendered."
          }
        }
      },
      "FeedbackFailure": {
        "type": "object",
        "required": [
          "code",
          "message",
          "app_message",
          "retry_allowed"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "not_feasible",
              "redraw_required",
              "images_not_allowed",
              "texts_not_allowed",
              "scene_changed",
              "scene_removed",
              "storyboard_replaced",
              "planning_interrupted",
              "planning_failed",
              "apply_failed",
              "insufficient_credits",
              "client_revoked",
              "client_expired",
              "scope_revoked",
              "production_unavailable",
              "stopped_in_app",
              "discarded_in_app",
              "cancelled_via_api"
            ],
            "description": "Reason (for `failed` and `discarded`). New codes may be added within v1; treat unknown codes like `apply_failed`."
          },
          "message": {
            "type": "string",
            "description": "English description of the reason."
          },
          "app_message": {
            "type": [
              "string",
              "null"
            ],
            "description": "The text mint shows in the chat (German), if any."
          },
          "retry_allowed": {
            "type": "boolean",
            "description": "`true` for `failed`: `POST …/retry` is possible."
          },
          "fixed_price_credits": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Only for `redraw_required`: the current fixed price of redrawing the scene from the mint price list (may change). Retry with `allow_redraw: true` to accept it."
          }
        }
      },
      "Feedback": {
        "type": "object",
        "required": [
          "object",
          "id",
          "batch_id",
          "film_id",
          "scene",
          "at_s",
          "scene_offset_s",
          "preview_id",
          "storyboard_version",
          "kind",
          "message",
          "region",
          "point",
          "attachments",
          "allow_redraw",
          "external_ref",
          "status",
          "stage",
          "queue_reason",
          "warnings",
          "result",
          "failure",
          "sender",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "feedback"
          },
          "id": {
            "type": "string",
            "pattern": "^fb_[0-9a-f]{32}$"
          },
          "batch_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the request (`id` of the batch)."
          },
          "film_id": {
            "type": "string",
            "description": "Id of the film."
          },
          "scene": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Number of the scene."
          },
          "at_s": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Time from the request."
          },
          "scene_offset_s": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Time within the scene (seconds from its start)."
          },
          "preview_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Preview the time refers to."
          },
          "storyboard_version": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Storyboard version at the time of the request."
          },
          "kind": {
            "type": "string",
            "enum": [
              "text",
              "image",
              "layout",
              "general"
            ]
          },
          "message": {
            "type": "string",
            "description": "The feedback as sent."
          },
          "region": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FeedbackRegion"
              },
              {
                "type": "null"
              }
            ]
          },
          "point": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FeedbackPoint"
              },
              {
                "type": "null"
              }
            ]
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FeedbackAttachment"
            }
          },
          "allow_redraw": {
            "type": "boolean"
          },
          "external_ref": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "applying",
              "applied",
              "failed",
              "discarded"
            ],
            "description": "`queued`: waiting for the scene; `applying`: being planned or applied (see `stage`); `applied`: the scene was changed; `failed`: not applied (see `failure`, retry possible); `discarded`: cancelled via the API or stopped in mint."
          },
          "stage": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "planning",
              "waiting_for_scene",
              "applying",
              "rendering",
              null
            ],
            "description": "Only for `applying`: `planning`, `waiting_for_scene` (the scene is rendering), `applying`, `rendering`."
          },
          "queue_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "scene_busy",
              "storyboard_busy",
              "service_unavailable",
              null
            ],
            "description": "Only for `queued`: `scene_busy` (another change of the scene is running), `storyboard_busy` (the storyboard is being created), `service_unavailable` (retried automatically)."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Hints for the entry, e.g. `timeline_may_be_outdated` or `scene_changed_since_preview`. New values may be added."
          },
          "result": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FeedbackResult"
              },
              {
                "type": "null"
              }
            ]
          },
          "failure": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FeedbackFailure"
              },
              {
                "type": "null"
              }
            ]
          },
          "sender": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Person"
              },
              {
                "type": "null"
              }
            ],
            "x-personal-data": true,
            "description": "Who sent the entry: the API access that sent (or last retried) it. Personal data."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Time of the request (UTC)."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last change of the state (UTC)."
          }
        }
      },
      "FeedbackBatch": {
        "type": "object",
        "required": [
          "object",
          "id",
          "film_id",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "feedback_batch"
          },
          "id": {
            "type": "string",
            "pattern": "^fbb_[0-9a-f]{32}$",
            "description": "Id of the request; bound to its Idempotency-Key."
          },
          "film_id": {
            "type": "string",
            "description": "Id of the film."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Feedback"
            },
            "description": "The entries in the order of `entries`."
          }
        }
      },
      "FeedbackList": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Feedback"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to get the next page; `null` on the last page."
          }
        }
      },
      "Upload": {
        "type": "object",
        "required": [
          "object",
          "id",
          "filename",
          "content_type",
          "bytes",
          "sha256",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "upload"
          },
          "id": {
            "type": "string",
            "pattern": "^upl_[0-9a-f]{32}$",
            "description": "Id of the upload; use it in `attachments`."
          },
          "filename": {
            "type": "string",
            "maxLength": 160,
            "description": "Cleaned name of the file (display only)."
          },
          "content_type": {
            "type": "string",
            "enum": [
              "image/png",
              "image/jpeg",
              "image/webp",
              "video/mp4",
              "video/quicktime",
              "video/webm"
            ],
            "description": "Media type of the stored file (the real type of the content)."
          },
          "bytes": {
            "type": "integer",
            "minimum": 1,
            "description": "Size in bytes."
          },
          "sha256": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "SHA-256 of the file (hex)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Time of the first upload (UTC)."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Expiry (UTC): 7 days after creation or the last use."
          }
        }
      },
      "Comment": {
        "type": "object",
        "required": [
          "object",
          "id",
          "revision",
          "at_s",
          "x",
          "y",
          "scene",
          "message",
          "author",
          "status",
          "result",
          "attachments",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "comment"
          },
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,64}$",
            "description": "Id of the comment (for `comment_ids`)."
          },
          "revision": {
            "type": "integer",
            "minimum": 1,
            "description": "Revision of the film the comment belongs to."
          },
          "at_s": {
            "type": "number",
            "minimum": 0,
            "description": "Time in the film in seconds, without the brand intro."
          },
          "x": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Horizontal position in the picture (0 = left, 1 = right), if given."
          },
          "y": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Vertical position in the picture (0 = top, 1 = bottom), if given."
          },
          "scene": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Number of the scene at `at_s`, if known."
          },
          "message": {
            "type": "string",
            "maxLength": 2000,
            "description": "Text of the comment."
          },
          "author": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Person"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who wrote the comment: a user in mint or an API access of your company (`name` = name of the access, `email` = `null`). Personal data.",
            "x-personal-data": true
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "applied",
              "not_applied",
              "resolved"
            ],
            "description": "`open`; `in_progress` while a new revision is being created from it; `applied` or `not_applied` afterwards; `resolved` if marked as done in mint. New values may be added within v1."
          },
          "result": {
            "type": [
              "object",
              "null"
            ],
            "description": "Outcome of applying the comment (`null` until it was applied).",
            "properties": {
              "revision": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 1,
                "description": "Revision that applied the comment."
              },
              "summary": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "What mint changed (text from mint, may be German)."
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Why mint did not apply it, if so (text from mint, may be German)."
              }
            }
          },
          "attachments": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "type": "object",
              "required": [
                "upload_id",
                "media_id"
              ],
              "properties": {
                "upload_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "pattern": "^upl_[0-9a-f]{32}$",
                  "description": "Upload the attachment came from, if it was added through the API (`POST /uploads`); `null` for attachments added in mint."
                },
                "media_id": {
                  "type": "string",
                  "description": "Id of the attachment in the mint project (the same file always has the same id)."
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Creation time (UTC)."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last change (UTC)."
          }
        }
      },
      "CommentList": {
        "type": "object",
        "required": [
          "object",
          "revision",
          "data",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "revision": {
            "type": "integer",
            "minimum": 1,
            "description": "Revision the comments belong to."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Comment"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` (with the same `revision`) to get the next page; `null` on the last page."
          }
        }
      },
      "CommentCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "at_s",
          "message"
        ],
        "properties": {
          "at_s": {
            "type": "number",
            "minimum": 0,
            "description": "Time in the film in seconds, without the brand intro."
          },
          "x": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Optional horizontal position (0–1)."
          },
          "y": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Optional vertical position (0–1)."
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Text of the comment (trimmed)."
          },
          "attachments": {
            "type": [
              "array",
              "null"
            ],
            "maxItems": 5,
            "items": {
              "type": "string",
              "pattern": "^upl_[0-9a-f]{32}$"
            },
            "description": "Up to 5 upload ids of your company. Unknown ids and uploads of other companies: `400 upload_not_found`; expired: `410 upload_expired`."
          },
          "revision": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "description": "Optional: the revision you commented on. If it is not the latest finished one, the answer is `409 revision_locked`."
          }
        }
      },
      "CommentsApply": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "comment_ids": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            },
            "description": "Only these comments (all must still be open, otherwise `409 comments_not_open`). Without it: all open comments of the latest finished revision."
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "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`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Idempotent-Replayed": {
            "$ref": "#/components/headers/IdempotentReplayed"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, malformed, revoked or expired API key (`unauthorized`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Not allowed (`forbidden_scope`, `ip_not_allowed`, `company_inactive`, `api_not_enabled`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found (`not_found`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Idempotent-Replayed": {
            "$ref": "#/components/headers/IdempotentReplayed"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "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`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "Idempotent-Replayed": {
            "$ref": "#/components/headers/IdempotentReplayed"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Request body too large (`payload_too_large`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "Request body is not JSON (`unsupported_media_type`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "IdempotencyMismatch": {
        "description": "Idempotency-Key reused with a different request (`idempotency_mismatch`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests (`rate_limited`).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Internal": {
        "description": "Internal error (`internal`). Quote the request id when you contact support.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Temporarily unavailable (`service_unavailable`). Retry later (after `Retry-After` seconds, if sent).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "The company does not have enough credits for the requested automation (`insufficient_credits`). Nothing was created; add credits in mint and retry.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
