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

# Render the project head

> Submit the current project head to the render engine as a standard (billable) render. Idempotency-Key note: reusing a key returns the original job unconditionally -- the body (here, the project head at submission time) is not compared, so a key reused after the project changed still replays the earlier job rather than 409ing. The response carries an ADVISORY `ok` + `violations[]` lint of the head; the render is dispatched either way.



## OpenAPI

````yaml /openapi.json post /v1/projects/{project_id}/renders
openapi: 3.1.0
info:
  title: Framelane API
  description: >
    Framelane renders video and runs AI media tasks from a single declarative
    request.


    ### Getting access (self-serve, no human needed)

    You can provision your own workspace and API key end-to-end — no dashboard,
    no sales call:

    1. `POST /v1/signup` with a `workspace_name` and an `email` you control. You
    get an API key
       back immediately and a 6-digit code is emailed to that address.
    2. Read the code from that inbox and `POST /v1/signup/verify` with it. If
    you drive a
       programmatic inbox (e.g. an agent mail service), this whole loop runs unattended.
    3. Your key works the moment the email is verified. Until then every authed
    call returns
       `403 email_not_verified` — verification is the only gate, so an unverified workspace can
       never spend. Re-calling `POST /v1/signup` re-keys the workspace and requires re-verifying.

    `POST /v1/signup`, `POST /v1/signup/verify`, and `GET /v1/capabilities` need
    **no** API key.


    ### Authentication

    Send your API key as a bearer token: `Authorization: Bearer fl_live_...`.


    ### Core workflow

    1. **Get media in.** Pass any publicly accessible URL directly, or `POST
    /v1/uploads` to
       get a signed URL, then `PUT` your file to it.
    2. **Submit one JSON payload.** `POST /v1/renders` composes a whole scene
    (canvas +
       `elements[]` + `transitions[]`) in a single request; `POST /v1/tasks/{type}` runs an AI
       operation (transcribe).
    3. **Wait for completion.** Jobs are async: poll `GET
    /v1/{renders|tasks}/{id}` or register
       a webhook. Statuses end in `completed`, `failed`, or `cancelled`.
    4. **Fetch the result.** A render's artifact is a file: use `output.url` or
       `GET /v1/renders/{id}/download` for a short-lived signed URL. A task that returns data
       puts it on the job's `result` object — `transcribe` returns its transcript there, has no
       `output`, and `404`s on `/v1/tasks/{id}/download`.

    ### Discovering what's possible

    Call **`GET /v1/capabilities`** (no auth) for the machine-readable catalog
    of every effect,

    motion, transition, format, element type, and task parameter — each flagged
    with whether the

    renderer supports it, plus all numeric ranges and rate limits. Validate
    against it before

    submitting to avoid `422`s.


    ### Idempotency

    `POST` endpoints accept an `Idempotency-Key` header. The same key with the
    same body replays

    the original response (`200`); with a different body it returns `409
    Conflict`.


    ### Errors

    Every error has the shape `{"error": {"code", "message", "details"}}`. The
    machine-readable

    `code` (e.g. `source_not_found`, `quota_exceeded`, `codec_unsupported`) is
    stable — branch on

    it to self-correct.
  version: 0.2.0
servers:
  - url: https://api.framelane.io
    description: Production
security: []
tags:
  - name: capabilities
    description: Discover supported features, formats, and limits.
  - name: renders
    description: Compose and render video from a declarative timeline.
  - name: projects
    description: >-
      Stateful editing: read a composition, apply targeted ops, preview, and
      render the head.
  - name: preview
    description: >-
      Validate a composition for free and preview a frame or window cheaply,
      without a full render.
  - name: tasks
    description: 'Run AI media operations: transcription.'
  - name: uploads
    description: Get signed URLs to upload source media into Framelane storage.
  - name: webhooks
    description: Subscribe to job lifecycle events with signed delivery.
  - name: workspace
    description: Manage your workspace, usage, and assets.
  - name: api-keys
    description: Create and revoke API keys.
  - name: auth
    description: Session sync for the first-party console.
  - name: signup
    description: Create a workspace and verify email.
  - name: billing
    description: Manage your subscription and billing portal.
  - name: system
    description: Health, readiness, and version probes.
paths:
  /v1/projects/{project_id}/renders:
    post:
      tags:
        - projects
      summary: Render the project head
      description: >-
        Submit the current project head to the render engine as a standard
        (billable) render. Idempotency-Key note: reusing a key returns the
        original job unconditionally -- the body (here, the project head at
        submission time) is not compared, so a key reused after the project
        changed still replays the earlier job rather than 409ing. The response
        carries an ADVISORY `ok` + `violations[]` lint of the head; the render
        is dispatched either way.
      operationId: render_project
      parameters:
        - name: project_id
          in: path
          required: true
          schema:
            type: string
            title: Project Id
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            title: Idempotency-Key
      responses:
        '202':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RenderSubmitOut'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '404':
          description: Project not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '422':
          description: Validation error or unsupported feature.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      security:
        - ApiKey: []
components:
  schemas:
    RenderSubmitOut:
      properties:
        id:
          type: string
          title: Id
          description: 'Unique job ID. Prefix indicates type: `render_` or `task_`.'
          example: render_01J8QR2K5VKDGN2T4FBM3CZYX7
        kind:
          $ref: '#/components/schemas/JobKind'
          description: Whether this is a `render` or a `task`.
          example: render
        is_preview:
          type: boolean
          title: Is Preview
          description: >-
            True for a cheap preview render. Previews are not billed and are
            excluded from the renders list.
          default: false
          example: false
        task_type:
          anyOf:
            - $ref: '#/components/schemas/TaskType'
            - type: 'null'
          description: For `task` jobs only — the specific AI operation.
          example: transcribe
        workspace_id:
          type: string
          title: Workspace Id
          description: ID of the workspace that owns this job.
          example: ws_01J8QR2K5VKDGN2T4FBM3CZYX8
        status:
          $ref: '#/components/schemas/JobStatus'
          description: >-
            Current lifecycle state: `queued` → `processing` → `completed` /
            `failed` / `cancelled`.
          example: queued
        progress_percent:
          type: integer
          maximum: 100
          minimum: 0
          title: Progress Percent
          description: 0–100 progress indicator updated by the render engine.
          default: 0
          example: 0
        progress_stage:
          anyOf:
            - $ref: '#/components/schemas/ProgressStage'
            - type: 'null'
          description: Current processing phase within a job.
          example: compositing
        output:
          anyOf:
            - $ref: '#/components/schemas/JobOutput'
            - type: 'null'
          description: >-
            Populated once `status == completed`. Contains the signed artifact
            URL.
        result:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Result
          description: >-
            Structured JSON result for tasks that return data rather than a file
            — `transcribe` returns the transcript here (`text`, `words`,
            `utterances`, and any requested analysis). Populated once `status ==
            completed`; `null` for renders and for any job whose only artifact
            is the file at `output.url`.
          example:
            language_code: en
            text: Welcome back to the show.
        error:
          anyOf:
            - $ref: '#/components/schemas/JobError'
            - type: 'null'
          description: Populated when `status == failed`. Contains a structured error code.
        metadata:
          additionalProperties: true
          type: object
          title: Metadata
          description: >-
            Caller-supplied key-value pairs echoed back on every webhook and
            response.
          example:
            project_id: proj_123
            user_ref: usr_abc
        created_at:
          type: string
          format: date-time
          title: Created At
          description: ISO-8601 UTC timestamp when the job was created.
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: ISO-8601 UTC timestamp of the last status change.
        completed_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Completed At
          description: ISO-8601 UTC timestamp when the job reached a terminal state.
        ok:
          type: boolean
          title: Ok
          description: >-
            Advisory — the render was still accepted and dispatched. False means
            the composition has error-severity findings that will likely make
            the output wrong (a self-transition, a fully off-canvas element); it
            does NOT mean the submission was refused. Everything that actually
            blocks a render is a 422 before you ever see this field.
          example: true
        violations:
          items:
            $ref: '#/components/schemas/Violation'
          type: array
          title: Violations
          description: >-
            Advisory — the render was still accepted and dispatched.
            Cross-element and layout findings from the same linter the preview
            dry-run runs, attached so you can see problems on the render you
            just paid for instead of only in the artifact. To catch these BEFORE
            spending a render, call POST /v1/preview with dry_run.Truncated to
            the first 40 findings (errors first) on very large compositions,
            with a VIOLATIONS_TRUNCATED marker carrying the full count; the free
            dry run is never truncated.
      type: object
      required:
        - id
        - kind
        - workspace_id
        - status
        - created_at
        - updated_at
        - ok
      title: RenderSubmitOut
      description: >-
        A freshly submitted render: the job row plus the advisory lint of its
        composition.


        Returned only by the two submit routes (``POST /v1/renders``,

        ``POST /v1/projects/{id}/renders``). A *subclass* rather than two more
        fields on

        :class:`JobOut`, because ``JobOut`` is the shape of a job row on every
        read route

        (``GET /v1/renders``, all ``/v1/tasks`` routes, the MCP read tools)
        where a lint

        result does not exist and would be permanently null.
    ApiError:
      description: >-
        The error envelope returned on every 4xx/5xx response: `{"error":
        {...}}`.
      properties:
        error:
          $ref: '#/components/schemas/ErrorResponse'
      required:
        - error
      title: ApiError
      type: object
    JobKind:
      type: string
      enum:
        - render
        - task
      title: JobKind
    TaskType:
      type: string
      enum:
        - transcribe
      title: TaskType
      description: >-
        The AI task types the API offers.


        Values:

        - `transcribe` — Speech-to-text transcription (via AssemblyAI) with
        optional analysis. Returns the transcript inline on the task's `result`
        object — there is no file artifact, so `output` is null and
        `/v1/tasks/{id}/download` returns 404.
      x-enumDescriptions:
        transcribe: >-
          Speech-to-text transcription (via AssemblyAI) with optional analysis.
          Returns the transcript inline on the task's `result` object — there is
          no file artifact, so `output` is null and `/v1/tasks/{id}/download`
          returns 404.
    JobStatus:
      type: string
      enum:
        - ingesting
        - queued
        - processing
        - completed
        - failed
        - cancelled
      title: JobStatus
      description: >-
        Values:

        - `ingesting` — External source files are being copied into Framelane
        storage.

        - `queued` — Accepted and waiting for a worker to pick it up.

        - `processing` — Actively rendering or processing.

        - `completed` — Finished successfully; the output artifact is available.

        - `failed` — Terminated with an error (see `error`).

        - `cancelled` — Cancelled by the caller before completion.
      x-enumDescriptions:
        ingesting: External source files are being copied into Framelane storage.
        queued: Accepted and waiting for a worker to pick it up.
        processing: Actively rendering or processing.
        completed: Finished successfully; the output artifact is available.
        failed: Terminated with an error (see `error`).
        cancelled: Cancelled by the caller before completion.
    ProgressStage:
      type: string
      enum:
        - downloading
        - compositing
        - encoding
        - uploading
      title: ProgressStage
      description: |-
        Values:
        - `downloading` — Fetching source media.
        - `compositing` — Compositing the scene on the GPU.
        - `encoding` — Encoding the output video.
        - `uploading` — Uploading the finished artifact to storage.
      x-enumDescriptions:
        downloading: Fetching source media.
        compositing: Compositing the scene on the GPU.
        encoding: Encoding the output video.
        uploading: Uploading the finished artifact to storage.
    JobOutput:
      properties:
        url:
          anyOf:
            - type: string
            - type: 'null'
          title: Url
          description: CDN URL to the completed artifact.
          example: https://cdn-user.framelane.io/render_01J.../output.mp4
        width:
          anyOf:
            - type: integer
            - type: 'null'
          title: Width
          description: Output video width in pixels.
          example: 1920
        height:
          anyOf:
            - type: integer
            - type: 'null'
          title: Height
          description: Output video height in pixels.
          example: 1080
        duration:
          anyOf:
            - type: number
            - type: 'null'
          title: Duration
          description: Output duration in seconds.
          example: 15.3
        size_bytes:
          anyOf:
            - type: integer
            - type: 'null'
          title: Size Bytes
          description: Artifact file size in bytes.
          example: 12582912
      type: object
      title: JobOutput
    JobError:
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
          description: Machine-readable error code.
          example: invalid_source
        message:
          type: string
          title: Message
          description: Human-readable error description.
          example: Source URL returned 404.
      type: object
      required:
        - code
        - message
      title: JobError
    Violation:
      properties:
        code:
          type: string
          title: Code
          description: Stable machine code, e.g. 'DANGLING_TRANSITION'.
        message:
          type: string
          title: Message
          description: Human-readable explanation.
        severity:
          $ref: '#/components/schemas/Severity'
          description: '''error'' blocks a good render; ''warning'' is advisory.'
          default: error
        path:
          anyOf:
            - type: string
            - type: 'null'
          title: Path
          description: Where it happened, e.g. 'elements[2]' or 'transitions[0].from_id'.
        bbox:
          anyOf:
            - items:
                type: number
              type: array
            - type: 'null'
          title: Bbox
          description: >-
            Canvas-space box [x, y, w, h] in pixels for layout findings (B1);
            null otherwise.
        time:
          anyOf:
            - type: number
            - type: 'null'
          title: Time
          description: >-
            Timeline second at which the finding applies; null for
            whole-timeline findings.
      type: object
      required:
        - code
        - message
      title: Violation
    ErrorResponse:
      description: >-
        The structured error body — the value of the `error` key on an error
        response.
      properties:
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          title: Message
          type: string
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          default: null
          title: Details
      required:
        - code
        - message
      title: ErrorResponse
      type: object
    ErrorCode:
      type: string
      enum:
        - invalid_source
        - source_not_found
        - source_too_large
        - asset_not_ready
        - codec_unsupported
        - decode_failed
        - encode_failed
        - timeout
        - out_of_memory
        - gpu_error
        - upload_failed
        - provider_error
        - ingest_failed
        - internal
        - cancelled
      title: ErrorCode
      description: >-
        Structured error codes surfaced on Job.error_code.


        Values:

        - `invalid_source` — A source URL was malformed or inaccessible.

        - `source_not_found` — A source URL returned 404.

        - `source_too_large` — A source file exceeded the maximum allowed size.

        - `asset_not_ready` — A referenced asset was still ingesting when the
        job ran.

        - `codec_unsupported` — A source used a codec the renderer cannot
        decode.

        - `decode_failed` — A source file could not be decoded.

        - `encode_failed` — The output could not be encoded.

        - `timeout` — Processing exceeded the time limit.

        - `out_of_memory` — The renderer ran out of GPU memory.

        - `gpu_error` — A GPU hardware/driver error occurred.

        - `upload_failed` — The output artifact failed to upload to storage.

        - `provider_error` — An upstream provider (e.g. AssemblyAI) returned an
        error.

        - `ingest_failed` — Copying an external source into storage failed.

        - `internal` — An unexpected internal error occurred.

        - `cancelled` — The job was cancelled.
      x-enumDescriptions:
        invalid_source: A source URL was malformed or inaccessible.
        source_not_found: A source URL returned 404.
        source_too_large: A source file exceeded the maximum allowed size.
        asset_not_ready: A referenced asset was still ingesting when the job ran.
        codec_unsupported: A source used a codec the renderer cannot decode.
        decode_failed: A source file could not be decoded.
        encode_failed: The output could not be encoded.
        timeout: Processing exceeded the time limit.
        out_of_memory: The renderer ran out of GPU memory.
        gpu_error: A GPU hardware/driver error occurred.
        upload_failed: The output artifact failed to upload to storage.
        provider_error: An upstream provider (e.g. AssemblyAI) returned an error.
        ingest_failed: Copying an external source into storage failed.
        internal: An unexpected internal error occurred.
        cancelled: The job was cancelled.
    Severity:
      type: string
      enum:
        - error
        - warning
      title: Severity
    ApiErrorCode:
      description: >-
        Machine-readable error code returned on a 4xx/5xx response. Branch on
        `error.code` to

        handle failures programmatically.


        Values:

        - `invalid_request` — The request was malformed or failed validation.

        - `unauthorized` — Missing or invalid API key.

        - `forbidden` — Authenticated but not permitted to perform this action.

        - `not_found` — The requested resource does not exist.

        - `conflict` — Conflicts with current state (e.g. a reused
        Idempotency-Key with a different body).

        - `unsupported_feature` — The composition uses a feature the renderer
        does not support.

        - `invalid_source` — A source URL is malformed or unreachable.

        - `source_not_found` — A referenced source file could not be found.

        - `source_too_large` — A source file exceeds the allowed size.

        - `asset_not_ready` — A referenced asset is still ingesting and is not
        ready yet.

        - `ingest_failed` — Copying an external source into Framelane storage
        failed.

        - `codec_unsupported` — A source uses a codec the renderer cannot
        decode.

        - `quota_exceeded` — The workspace has exhausted its plan quota (HTTP
        402).

        - `rate_limited` — Too many requests; retry after the Retry-After
        interval.

        - `internal` — An unexpected server error occurred.

        - `email_not_verified` — The workspace email is unverified; verify it
        before making authed calls.

        - `email_in_use` — The email is already associated with another
        workspace.

        - `invalid_otp` — The verification code is incorrect or expired.

        - `unsupported_content_type` — The provided content type is not
        supported.

        - `http_error` — A generic HTTP error not covered by a more specific
        code.
      enum:
        - invalid_request
        - unauthorized
        - forbidden
        - not_found
        - conflict
        - unsupported_feature
        - invalid_source
        - source_not_found
        - source_too_large
        - asset_not_ready
        - ingest_failed
        - codec_unsupported
        - quota_exceeded
        - rate_limited
        - internal
        - email_not_verified
        - email_in_use
        - invalid_otp
        - unsupported_content_type
        - http_error
      title: ErrorCode
      type: string
      x-enumDescriptions:
        invalid_request: The request was malformed or failed validation.
        unauthorized: Missing or invalid API key.
        forbidden: Authenticated but not permitted to perform this action.
        not_found: The requested resource does not exist.
        conflict: >-
          Conflicts with current state (e.g. a reused Idempotency-Key with a
          different body).
        unsupported_feature: The composition uses a feature the renderer does not support.
        invalid_source: A source URL is malformed or unreachable.
        source_not_found: A referenced source file could not be found.
        source_too_large: A source file exceeds the allowed size.
        asset_not_ready: A referenced asset is still ingesting and is not ready yet.
        ingest_failed: Copying an external source into Framelane storage failed.
        codec_unsupported: A source uses a codec the renderer cannot decode.
        quota_exceeded: The workspace has exhausted its plan quota (HTTP 402).
        rate_limited: Too many requests; retry after the Retry-After interval.
        internal: An unexpected server error occurred.
        email_not_verified: >-
          The workspace email is unverified; verify it before making authed
          calls.
        email_in_use: The email is already associated with another workspace.
        invalid_otp: The verification code is incorrect or expired.
        unsupported_content_type: The provided content type is not supported.
        http_error: A generic HTTP error not covered by a more specific code.
  securitySchemes:
    ApiKey:
      type: http
      scheme: bearer

````