> ## 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.

# Publish a template

> Admin workspaces only. List one of your template projects in the gallery. `status: published` makes it public, which needs every media URL in its composition on the shared CDN (`template_assets_not_shared` lists the rest) and no `webhook_url` (`template_has_webhook`), since every copy inherits it; a `draft` is visible to admin workspaces only.



## OpenAPI

````yaml /openapi.json post /v1/templates
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: templates
    description: >-
      Browse the template gallery, and manage the public catalogue (admin
      workspaces).
  - 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/templates:
    post:
      tags:
        - templates
      summary: Publish a template
      description: >-
        Admin workspaces only. List one of your template projects in the
        gallery. `status: published` makes it public, which needs every media
        URL in its composition on the shared CDN (`template_assets_not_shared`
        lists the rest) and no `webhook_url` (`template_has_webhook`), since
        every copy inherits it; a `draft` is visible to admin workspaces only.
      operationId: publish_template
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TemplateListingCreate'
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TemplateListingOut'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '403':
          description: 'admin_only: not an admin workspace.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '404':
          description: project_id is not a project in your workspace.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '409':
          description: The slug is taken, or the project already has a listing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '422':
          description: >-
            Not a template, or template_assets_not_shared or
            template_has_webhook when publishing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      security:
        - ApiKey: []
components:
  schemas:
    TemplateListingCreate:
      properties:
        project_id:
          type: string
          title: Project Id
          description: A kind=template project in your workspace.
        slug:
          type: string
          maxLength: 80
          pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
          title: Slug
          description: Stable public handle, e.g. `stock-montage`.
        title:
          type: string
          maxLength: 255
          minLength: 1
          title: Title
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
        category:
          anyOf:
            - type: string
              maxLength: 64
            - type: 'null'
          title: Category
        tags:
          items:
            type: string
          type: array
          title: Tags
        priority:
          type: integer
          title: Priority
          description: Sorts rows and carousels, highest first.
          default: 0
        premium:
          type: boolean
          title: Premium
          default: false
        status:
          type: string
          enum:
            - draft
            - published
          title: Status
          description: >-
            `published` makes the template public. Publishing needs every media
            URL in its composition to sit on the shared CDN (422
            template_assets_not_shared) and no webhook_url (422
            template_has_webhook).
          default: draft
        preview_url:
          anyOf:
            - type: string
              maxLength: 2083
              minLength: 1
              format: uri
            - type: 'null'
          title: Preview Url
          description: A finished render of the template.
        family:
          anyOf:
            - type: string
              maxLength: 64
            - type: 'null'
          title: Family
          description: Links the size variants of one design.
      additionalProperties: false
      type: object
      required:
        - project_id
        - slug
        - title
      title: TemplateListingCreate
    TemplateListingOut:
      properties:
        id:
          type: string
          title: Id
          description: Listing id.
        project_id:
          type: string
          title: Project Id
          description: The listed template project.
        slug:
          type: string
          title: Slug
        title:
          type: string
          title: Title
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
        category:
          anyOf:
            - type: string
            - type: 'null'
          title: Category
        tags:
          items:
            type: string
          type: array
          title: Tags
        priority:
          type: integer
          title: Priority
        premium:
          type: boolean
          title: Premium
        status:
          type: string
          enum:
            - draft
            - published
          title: Status
        preview_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Preview Url
        published_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Published At
        family:
          anyOf:
            - type: string
            - type: 'null'
          title: Family
        created_at:
          type: string
          format: date-time
          title: Created At
        updated_at:
          type: string
          format: date-time
          title: Updated At
      type: object
      required:
        - id
        - project_id
        - slug
        - title
        - priority
        - premium
        - status
        - created_at
        - updated_at
      title: TemplateListingOut
      description: A gallery listing as stored.
    ApiError:
      description: >-
        The error envelope returned on every 4xx/5xx response: `{"error":
        {...}}`.
      properties:
        error:
          $ref: '#/components/schemas/ErrorResponse'
      required:
        - error
      title: ApiError
      type: object
    ErrorResponse:
      description: >-
        The structured error body — the value of the `error` key on an error
        response.
      properties:
        code:
          $ref: '#/components/schemas/ApiErrorCode'
        message:
          type: string
          title: Message
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          default: null
          title: Details
      required:
        - code
        - message
      title: ErrorResponse
      type: object
    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.

        - `plan_required` — The feature needs a higher plan (HTTP 402);
        `details.required_plan` names it.

        - `admin_only` — Only an admin workspace may call this route (HTTP 403).

        - `template_assets_not_shared` — A published template may reference only
        shared CDN media; `details.urls` lists the rest.

        - `template_has_webhook` — A published template may not carry a
        `webhook_url`: every copy of it would notify that URL.

        - `http_error` — A generic HTTP error not covered by a more specific
        code.

        - `shader_compile_error` — A shader's SkSL did not compile;
        `details.errors` gives each error's line and column.

        - `shader_compiler_unavailable` — The shader compiler is not installed
        on this deployment or did not answer in time (HTTP 503).

        - `shader_not_found` — An effect names a shader id that is neither this
        workspace's nor shared.

        - `shader_param_invalid` — An effect sets a shader param the shader does
        not declare, or a value of the wrong type or range.
      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
        - inline_too_large
        - plan_required
        - admin_only
        - template_assets_not_shared
        - template_has_webhook
        - http_error
        - shader_compile_error
        - shader_compiler_unavailable
        - shader_not_found
        - shader_param_invalid
      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.
        plan_required: >-
          The feature needs a higher plan (HTTP 402); `details.required_plan`
          names it.
        admin_only: Only an admin workspace may call this route (HTTP 403).
        template_assets_not_shared: >-
          A published template may reference only shared CDN media;
          `details.urls` lists the rest.
        template_has_webhook: >-
          A published template may not carry a `webhook_url`: every copy of it
          would notify that URL.
        http_error: A generic HTTP error not covered by a more specific code.
        shader_compile_error: >-
          A shader's SkSL did not compile; `details.errors` gives each error's
          line and column.
        shader_compiler_unavailable: >-
          The shader compiler is not installed on this deployment or did not
          answer in time (HTTP 503).
        shader_not_found: >-
          An effect names a shader id that is neither this workspace's nor
          shared.
        shader_param_invalid: >-
          An effect sets a shader param the shader does not declare, or a value
          of the wrong type or range.
  securitySchemes:
    ApiKey:
      type: http
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.