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

# Export Canvas

> Export a canvas as PNG, JPEG, PDF, PPTX, MP4, or GIF. Returns a signed URL or a polling handle.

Exports require DB-level team access regardless of share-link status.
Share-link-only callers can read the canvas via ``GET /canvases/{id}``
with a ``share_token`` query param but cannot export.

Large or slow exports (multi-page documents, mp4/gif animation renders)
may exceed the synchronous wait budget; in that case the response carries
``status='in_progress'`` with a ``task_id`` — call
``GET /canvases/{id}/export-status?task_id=...`` to retrieve the signed
URL once the background export finishes. Animation exports render one
page's timeline server-side (silent MP4; page-timeline duration, capped
at 2000 frames / 120 s) and reject a page with nothing to animate with a
422 ``no_animation`` error.



## OpenAPI

````yaml /openapi/moda-public-api.yaml post /canvases/{canvas_id}/export
openapi: 3.1.0
info:
  title: Moda Public API
  description: >
    Programmatic access to Moda's canvas design platform. Create designs, export
    assets, manage brand kits, and run AI design tasks.


    ## Versioning


    Pin response shapes with a calendar-dated `Moda-Version` header (e.g.
    `2026-05-01`). **The pin is global** — it applies to every endpoint in the
    request, not just the one you adopted it for, so raising it to use a new
    endpoint also moves your other responses to that version's shapes.


    ### Migrating `2026-04-12` → `2026-05-01`


    - **Tasks** move from the flat `JobResponse` (`job_id`, `canvas_url`,
    `can_export`) to the canonical `Task` envelope. The export artifact is now
    under `result.export` as `{url, format, page_count}`; update any code that
    read the export URL from the old top-level fields.

    - **Multi-page PNG/JPEG exports are delivered as a single `.zip`** of
    per-page files (`page-1.png`, …), so `result.export.format` is `zip`. For
    one bundled document instead, request `format=pdf` or `pptx`; for a single
    image, pass `page_number`.
  version: 1.0.0
  x-moda-api-version: '2026-05-01'
servers:
  - url: https://api.moda.app/v1
    description: Production
security: []
tags:
  - name: canvases
    description: List, search, read, export, and share canvases
  - name: tasks
    description: Start and monitor AI design tasks
  - name: organizations
    description: List organizations and teams
  - name: credits
    description: Check credit balance and usage
  - name: brand-kits
    description: Manage brand kits
  - name: remix
    description: Duplicate and edit canvases
  - name: share-links
    description: Resolve share URLs to canvas identifiers
  - name: uploads
    description: Upload files for use as attachments
  - name: usage
    description: Aggregate API usage stats for the caller's team
  - name: embed
    description: Signed iframe embed sessions
  - name: web
    description: 'Metered web research: search and page reading'
  - name: websites
    description: >-
      Create, update, publish, screenshot, and manage hosted multi-page static
      sites
  - name: drive
    description: >-
      Organize the workspace: folders, the folder tree, and item
      move/rename/visibility/delete for folders, canvases, and files
paths:
  /canvases/{canvas_id}/export:
    post:
      tags:
        - canvases
      summary: Export Canvas
      description: >-
        Export a canvas as PNG, JPEG, PDF, PPTX, MP4, or GIF. Returns a signed
        URL or a polling handle.


        Exports require DB-level team access regardless of share-link status.

        Share-link-only callers can read the canvas via ``GET /canvases/{id}``

        with a ``share_token`` query param but cannot export.


        Large or slow exports (multi-page documents, mp4/gif animation renders)

        may exceed the synchronous wait budget; in that case the response
        carries

        ``status='in_progress'`` with a ``task_id`` — call

        ``GET /canvases/{id}/export-status?task_id=...`` to retrieve the signed

        URL once the background export finishes. Animation exports render one

        page's timeline server-side (silent MP4; page-timeline duration, capped

        at 2000 frames / 120 s) and reject a page with nothing to animate with a

        422 ``no_animation`` error.
      operationId: exportCanvas
      parameters:
        - name: canvas_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^cvs_[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$
            description: >-
              Prefixed ``cvs_`` wire ID (Crockford base32 body) — the canonical,
              recommended form. For back-compat, **a bare UUID string is also
              accepted** in both path parameters and JSON request bodies (older
              integrations that stored raw UUIDs keep working). Both are
              permanent, supported inputs.
            example: cvs_01HT9WK8N3M2J4A5Z6P7Q8R9TV
            title: Canvas Id
        - name: format
          in: query
          required: false
          schema:
            type: string
            description: >-
              Export format: png, jpeg, pdf, pptx, mp4, or gif. mp4/gif render a
              single page's animation timeline (server MP4s are silent) and
              require the page to actually carry animation — a still page is
              rejected with ``no_animation``.
            default: png
            title: Format
          description: >-
            Export format: png, jpeg, pdf, pptx, mp4, or gif. mp4/gif render a
            single page's animation timeline (server MP4s are silent) and
            require the page to actually carry animation — a still page is
            rejected with ``no_animation``.
        - name: page_number
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
                minimum: 1
              - type: 'null'
            description: >-
              1-indexed page number to export. Omit to export all pages —
              PDF/PPTX bundle every page natively, while multi-page PNG/JPEG are
              returned as a ``.zip`` of per-page files (``page-1.png``,
              ``page-2.png``, …) since a single image container can't hold
              multiple pages. ``format`` in the response reflects what was
              actually delivered (``zip`` in the bundled case). mp4/gif export
              exactly one page: required on a multi-page canvas, defaulted to 1
              on a single-page one.
            title: Page Number
          description: >-
            1-indexed page number to export. Omit to export all pages — PDF/PPTX
            bundle every page natively, while multi-page PNG/JPEG are returned
            as a ``.zip`` of per-page files (``page-1.png``, ``page-2.png``, …)
            since a single image container can't hold multiple pages. ``format``
            in the response reflects what was actually delivered (``zip`` in the
            bundled case). mp4/gif export exactly one page: required on a
            multi-page canvas, defaulted to 1 on a single-page one.
        - name: pixel_ratio
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
                maximum: 4
                minimum: 1
              - type: 'null'
            description: >-
              Render scale multiplier (1-4). Higher values produce sharper
              exports at larger file sizes. Supported for PNG, JPEG, PDF, MP4,
              and GIF exports (mp4/gif default to 1 — page resolution). PPTX
              ignores this parameter.
            title: Pixel Ratio
          description: >-
            Render scale multiplier (1-4). Higher values produce sharper exports
            at larger file sizes. Supported for PNG, JPEG, PDF, MP4, and GIF
            exports (mp4/gif default to 1 — page resolution). PPTX ignores this
            parameter.
        - name: flatten
          in: query
          required: false
          schema:
            type: boolean
            description: >-
              PDF only: produce a raster-only PDF with no searchable text,
              vector elements, or hyperlinks.
            default: true
            title: Flatten
          description: >-
            PDF only: produce a raster-only PDF with no searchable text, vector
            elements, or hyperlinks.
        - name: wait
          in: query
          required: false
          schema:
            type: boolean
            description: >-
              When True (default), block up to ~20s for the export to finish
              before returning an in-progress handle. When False, return
              ``status='in_progress'`` with a ``task_id`` immediately — poll
              ``/canvases/{id}/export-status`` until terminal.
            default: true
            title: Wait
          description: >-
            When True (default), block up to ~20s for the export to finish
            before returning an in-progress handle. When False, return
            ``status='in_progress'`` with a ``task_id`` immediately — poll
            ``/canvases/{id}/export-status`` until terminal.
        - name: force_refresh
          in: query
          required: false
          schema:
            type: boolean
            description: >-
              When False (default), reuse a cached export if the canvas is
              unchanged since it was rendered — fast, no browser render. Set
              True to force a fresh render.
            default: false
            title: Force Refresh
          description: >-
            When False (default), reuse a cached export if the canvas is
            unchanged since it was rendered — fast, no browser render. Set True
            to force a fresh render.
        - $ref: '#/components/parameters/ModaVersion'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExportCanvasResponse'
        '401':
          description: Authentication required.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Permission denied for this scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: Resource not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: Conflict (idempotency / resource state).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: Request validation failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: Internal error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - API Key: []
components:
  parameters:
    ModaVersion:
      name: Moda-Version
      in: header
      required: false
      description: >-
        Calendar-dated API version pin. New integrations should pin `2026-05-01`
        to opt into the newest response shapes. For back-compat the server also
        accepts requests with no header and resolves them to the current default
        (today: `2026-04-12`); that default advances on each [sunset
        date](/api-reference/versioning). Any unsupported value returns 400
        `unsupported_version`.
      example: '2026-05-01'
      schema:
        type: string
        enum:
          - '2026-04-12'
          - '2026-05-01'
        default: '2026-05-01'
        example: '2026-05-01'
  schemas:
    ExportCanvasResponse:
      properties:
        status:
          type: string
          title: Status
          description: >-
            ``completed`` when ``url`` is set, or ``in_progress`` when polling
            is required.
          default: completed
        canvas_id:
          type: string
          pattern: ^cvs_[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$
          title: Canvas Id
          description: Prefixed canvas identifier for the canvas that was exported.
          example: cvs_01HT9WK8N3M2J4A5Z6P7Q8R9TV
        canvas_url:
          type: string
          title: Canvas Url
          description: Full URL to open the source canvas in the Moda editor.
        url:
          anyOf:
            - type: string
            - type: 'null'
          title: Url
          description: >-
            Signed download URL for the exported file (set when
            ``status='completed'``). Expires in 7 days.
        format:
          type: string
          title: Format
          description: >-
            Delivered format — ``png``, ``jpeg``, ``pdf``, ``pptx``, ``mp4``,
            ``gif``, or ``zip``. A multi-page PNG/JPEG export (``page_number``
            omitted, canvas has >1 page) is bundled into a ``.zip`` of per-page
            files, so ``format`` is ``zip`` even though the request asked for an
            image format.
        task_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Task Id
          description: >-
            Export task ID set when ``status='in_progress'``; poll the
            export-status endpoint with it.
        retry_after_seconds:
          anyOf:
            - type: integer
            - type: 'null'
          title: Retry After Seconds
          description: Suggested poll interval when ``status='in_progress'``.
        total_pages:
          anyOf:
            - type: integer
            - type: 'null'
          title: Total Pages
          description: >-
            Total page count for the source canvas (echoed for client
            convenience).
        source:
          anyOf:
            - type: string
            - type: 'null'
          title: Source
          description: >-
            How the export was served: ``cache`` (existing artifact reused),
            ``slice`` (page extracted from a cached document, no render), or
            ``render`` (freshly rendered). ``null`` while
            ``status='in_progress'``.
        warnings:
          items:
            $ref: '#/components/schemas/ExportWarning'
          type: array
          title: Warnings
          description: >-
            Quality caveats about the delivered file — empty when nothing was
            degraded. Set on ``status='completed'`` only (an in-progress
            response makes no claim yet); a cached or sliced artifact carries
            the same lane-level caveats as a freshly rendered one.
      type: object
      required:
        - canvas_id
        - canvas_url
        - format
      title: ExportCanvasResponse
      description: |-
        Response for ``POST /canvases/{id}/export``.

        Carries one of two shapes depending on ``status``:

        - ``completed`` — ``url`` is the signed download URL.
        - ``in_progress`` — ``task_id`` lets the caller poll
          ``GET /canvases/{id}/export-status?task_id=...`` until terminal.
    ErrorEnvelope:
      properties:
        type:
          $ref: '#/components/schemas/ErrorType'
        code:
          type: string
          title: Code
          description: >-
            Stable machine string keyed to doc_url. Never changes once
            published.
        message:
          type: string
          title: Message
          description: >-
            Human-readable message, for developers. Not localized, not
            user-facing.
        doc_url:
          type: string
          title: Doc Url
          description: Permalink to the docs page for this code.
        request_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Request Id
          description: Correlator for logs/Sentry/audit.
        causes:
          anyOf:
            - items:
                $ref: '#/components/schemas/ErrorEnvelope'
              type: array
            - type: 'null'
          title: Causes
          description: Aggregated upstream failures (e.g. per-page export errors).
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Details
          description: >-
            Code-specific detail payload (e.g. validation field list). Callers
            should pass ``None`` rather than ``{}`` to omit the key from the
            response.
        retry_after_ms:
          anyOf:
            - type: integer
            - type: 'null'
          title: Retry After Ms
          description: Hint for rate-limited or transient errors.
        retryable:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Retryable
          description: >-
            Explicit retryability for this code, from the error-code registry:
            ``false`` means retrying the same request cannot succeed (fix the
            input / resource first); ``true`` means the condition is transient
            and a retry (with backoff / after the blocker clears) can succeed.
            Omitted for codes whose retryability is context-dependent — do not
            derive it from ``type`` or the HTTP status when this field is
            present.
      type: object
      required:
        - type
        - code
        - message
        - doc_url
      title: ErrorEnvelope
      description: >-
        Canonical error payload, nested under the top-level ``{"error": ...}``
        key.
    ExportWarning:
      properties:
        code:
          type: string
          title: Code
          description: >-
            Machine-readable warning code. Current codes:
            ``pptx_shape_rasterized`` (images that could not be embedded as
            native PowerPoint pictures — where the raster fallback covered them
            the pixels are baked into the slide background and the shapes are
            not editable, and where it did not those pictures may be missing),
            ``pptx_content_dropped`` (elements are MISSING from the deck
            entirely), and ``pdf_links_flattened`` (the server PDF lane emits no
            link annotations, so hyperlinks on the exported pages are not
            clickable). New codes are added over time — treat an unrecognized
            code as informational and show ``message``.
        message:
          type: string
          title: Message
          description: Human-readable explanation naming exactly what was degraded.
        severity:
          type: string
          title: Severity
          description: '``warning`` today — the export succeeded but something was degraded.'
          default: warning
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Details
          description: >-
            Structured, code-specific specifics — e.g. ``page_numbers`` and
            ``link_count`` for ``pdf_links_flattened``, ``dropped_image_count``
            / ``dropped_image_reasons`` / ``dropped_image_urls`` for
            ``pptx_shape_rasterized``, ``dropped_node_ids`` for
            ``pptx_content_dropped``. ``null`` when the code carries no extra
            data.
      type: object
      required:
        - code
        - message
      title: ExportWarning
      description: >-
        A quality caveat about an export that otherwise SUCCEEDED.


        A warning never means the file failed — it names something the export
        lane

        had to degrade that a caller would otherwise assume survived, so the
        caller

        can decide whether the artifact is good enough to ship.
    ErrorType:
      type: string
      enum:
        - invalid_request
        - authentication
        - permission
        - not_found
        - conflict
        - rate_limited
        - idempotency_conflict
        - unprocessable
        - upstream_error
        - internal_error
      title: ErrorType
      description: Closed set of high-level categories SDKs branch on.
  securitySchemes:
    API Key:
      type: http
      description: API key from Settings > Developer > REST API
      scheme: bearer

````