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

# Create Canvas

> Create a canvas — blank pages, or a one-call copy of a team template.

Returns page ids + editor URL + revision. With ``template_canvas_id`` set,
the new canvas is a full copy of that source canvas (every page, node, and
variable; description, category, and theme carried over; the source's brand
kit kept — brand consistency is the point of team templates) instead of
blank pages — the same copy seam the in-app agent's
``create_canvas(mode="copy_canvas")`` uses. The copy never inherits the
source's template flag, and ``name`` replaces the default copy naming.
Discover the team's flagged templates (with thumbnails) via
``GET /v1/templates``. A missing, deleted, or cross-team source is a 404
``not_found``; a source whose collaboration state is still syncing is a
retryable 503 ``canvas_crdt_state_not_ready``.

Honors ``idempotency_key`` through the shared pipeline: a retried create
replays the stored result instead of minting a second canvas; the same key
with a different payload is a 409 ``idempotency_conflict``.

Create-specific key semantics (deliberate, documented on the field):

- Replay is CLIENT-KEY-ONLY. Without a key every call creates a new
  canvas; the server never derives a replay key from the payload
  (same-args creates from different sessions are distinct intents).
- A replayed create is UNMISTAKABLY loud: ``replayed: true`` plus
  ``reused_existing: true``, the ORIGINAL ``created_at``, and a steering
  ``note`` — never a bare ``committed`` — because the replayed canvas may
  have been authored since, and a caller that treats it as a fresh blank
  canvas would author over existing content.
- A stored result whose canvas was DELETED since does not replay: the
  stale record is invalidated and the create re-executes fresh (loud
  ``stale_replay_invalidated`` marker), minting a NEW canvas.



## OpenAPI

````yaml /openapi/moda-public-api.yaml post /canvases
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:
    post:
      tags:
        - canvas-actions
      summary: Create Canvas
      description: >-
        Create a canvas — blank pages, or a one-call copy of a team template.


        Returns page ids + editor URL + revision. With ``template_canvas_id``
        set,

        the new canvas is a full copy of that source canvas (every page, node,
        and

        variable; description, category, and theme carried over; the source's
        brand

        kit kept — brand consistency is the point of team templates) instead of

        blank pages — the same copy seam the in-app agent's

        ``create_canvas(mode="copy_canvas")`` uses. The copy never inherits the

        source's template flag, and ``name`` replaces the default copy naming.

        Discover the team's flagged templates (with thumbnails) via

        ``GET /v1/templates``. A missing, deleted, or cross-team source is a 404

        ``not_found``; a source whose collaboration state is still syncing is a

        retryable 503 ``canvas_crdt_state_not_ready``.


        Honors ``idempotency_key`` through the shared pipeline: a retried create

        replays the stored result instead of minting a second canvas; the same
        key

        with a different payload is a 409 ``idempotency_conflict``.


        Create-specific key semantics (deliberate, documented on the field):


        - Replay is CLIENT-KEY-ONLY. Without a key every call creates a new
          canvas; the server never derives a replay key from the payload
          (same-args creates from different sessions are distinct intents).
        - A replayed create is UNMISTAKABLY loud: ``replayed: true`` plus
          ``reused_existing: true``, the ORIGINAL ``created_at``, and a steering
          ``note`` — never a bare ``committed`` — because the replayed canvas may
          have been authored since, and a caller that treats it as a fresh blank
          canvas would author over existing content.
        - A stored result whose canvas was DELETED since does not replay: the
          stale record is invalidated and the create re-executes fresh (loud
          ``stale_replay_invalidated`` marker), minting a NEW canvas.
      operationId: canvasActionsCreate
      parameters:
        - $ref: '#/components/parameters/ModaVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CanvasCreateRequest'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Canvasactionscreate
        '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:
    CanvasCreateRequest:
      properties:
        name:
          type: string
          maxLength: 300
          minLength: 1
          title: Name
          description: Canvas display name.
        width:
          type: integer
          maximum: 10000
          exclusiveMinimum: 0
          title: Width
          description: Page width in px.
          default: 1920
        height:
          type: integer
          maximum: 10000
          exclusiveMinimum: 0
          title: Height
          description: Page height in px.
          default: 1080
        page_count:
          type: integer
          maximum: 100
          minimum: 1
          title: Page Count
          description: Number of blank pages.
          default: 1
        category:
          anyOf:
            - type: string
            - type: 'null'
          title: Category
          description: >-
            Canvas category (slides, social, carousel, pdf, diagram, ui,
            animation, prints, web-ads, other). Drives slides/motion semantics
            and export defaults. Omit to let the server infer.
        template_canvas_id:
          anyOf:
            - 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
            - type: 'null'
          title: Template Canvas Id
          description: >-
            Create the canvas as a copy of this existing canvas (``cvs_`` wire
            id or bare UUID) instead of blank pages — the one-call
            template-sourced create. The source must be a live canvas in your
            team (list your team's flagged templates with ``GET /v1/templates``;
            any team canvas works as a source). The whole document is copied —
            every page, node, and variable, plus description, category, and
            theme — the copy keeps the source's brand kit, and ``name`` replaces
            the default copy naming. The copy is a regular canvas: it never
            inherits the source's template flag. Mutually exclusive with
            ``width``/``height``/``page_count``/``category`` (the source defines
            them).
        folder_id:
          anyOf:
            - type: string
              pattern: ^fld_[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$
              description: >-
                Prefixed ``fld_`` 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: fld_01HT9WK8N3M2J4A5Z6P7Q8R9TV
            - type: 'null'
          title: Folder Id
          description: >-
            Drive folder (``fld_...``) to create the canvas in. The folder must
            belong to your team; a canvas created into a folder adopts the
            folder's visibility. Omit to let the workspace's default save
            location decide placement.
        visibility:
          anyOf:
            - type: string
              enum:
                - team
                - private
            - type: 'null'
          title: Visibility
          description: >-
            Explicit visibility for the new canvas: ``private`` hides it from
            teammates; ``team`` makes it visible to the whole team. Ignored in
            favor of the folder's visibility when ``folder_id`` is set. Omit for
            the workspace default.
        idempotency_key:
          anyOf:
            - type: string
              maxLength: 200
            - type: 'null'
          title: Idempotency Key
          description: >-
            A retried create with the same key replays the stored result
            (replayed:true + reused_existing:true + the original created_at)
            instead of creating a second canvas. Replay is CLIENT-KEY-ONLY: omit
            the key and every call creates a NEW canvas — the server never
            derives a key from the payload. Never derive this key from the
            payload yourself (e.g. a hash of name+dimensions): same-args creates
            are legitimately distinct intents, and a payload-derived key reused
            from a later session returns the ORIGINAL (possibly authored) canvas
            instead of a new one. Use a fresh random key per intended create;
            reuse it only to retry that same create.
      additionalProperties: false
      type: object
      required:
        - name
      title: CanvasCreateRequest
    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.
    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

````