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

# Update the form theme

> **Required scope:** `form:write`. Partial-merge semantics — only keys present are written; explicit `null` clears.



## OpenAPI

````yaml /api-reference/openapi.yaml put /api/v1/forms/{form_token}/theme
openapi: 3.0.1
info:
  title: FormHug API V1
  version: v1
  description: |
    FormHug public REST API. All request/response keys are snake_case.

    Successful response:

    ```json
    {
      "data": { ... }
    }
    ```

    Paginated response:

    ```json
    {
      "data": [ ... ],
      "pagination": {
        "total": 123,
        "next_cursor": "NQ=="
      }
    }
    ```

    Error response:

    ```json
    {
      "error": "Human-readable message",
      "error_details": [
        { "attribute": "field_name", "message": "specific error" }
      ]
    }
    ```

    `error_details` only appears for model validation failures.
servers:
  - url: https://formhug.ai
security: []
tags:
  - name: Forms
    description: Form CRUD
  - name: Attachments
    description: >-
      2-step upload for form-element images (theme, rich-text, or field-element
      images). Prepare returns an `upload_id`; consume it in a theme update,
      attachment_commitments, or the form `fields` payload depending on purpose.
  - name: Entries
    description: Entries collected by a form owned by the current user
  - name: Folders
    description: Folder CRUD
  - name: Participated Forms
    description: Forms the current user has submitted to (does not own)
  - name: Published Form Entries
    description: Submit entries to a published form
  - name: Published Forms
    description: Read a published form's public structure for filling
  - name: Entry Attachments
    description: >-
      2-step upload for `attachment` field submissions. Prepare returns an
      `upload_id`; submit it under the attachment field in `field_values` when
      creating the entry.
  - name: Me
    description: Current authenticated user
  - name: Webhooks
    description: Webhook integrations attached to a form
  - name: Form Themes
    description: Read and update a form's visual theme
  - name: Form Settings
    description: >-
      Per-form settings — submission flow, availability, access control,
      presentation
  - name: Field Rules
    description: >-
      Read and replace a form's conditional field-display and post-submission
      redirect rules.
  - name: OAuth
    description: OAuth 2.0 PKCE token issuance and revocation
paths:
  /api/v1/forms/{form_token}/theme:
    parameters:
      - name: form_token
        in: path
        description: Form token
        required: true
        schema:
          type: string
    put:
      tags:
        - Form Themes
      summary: Update the form theme
      description: >-
        **Required scope:** `form:write`. Partial-merge semantics — only keys
        present are written; explicit `null` clears.
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FormThemeUpdateRequest'
      responses:
        '200':
          description: Null clears form_container.style.background_color
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    required:
                      - header
                      - wallpaper
                      - primary_color
                      - submit_button
                      - typography
                      - general_button
                    properties:
                      header:
                        type: object
                        required:
                          - images
                        properties:
                          images:
                            type: array
                            description: >-
                              Header images; empty array when no header image is
                              set.
                            items:
                              type: object
                              required:
                                - url
                                - id
                              properties:
                                url:
                                  type: string
                                  format: uri
                                  description: Header image visit URL at size xlarge.
                                id:
                                  type: string
                                  description: Attachment id.
                      wallpaper:
                        type: object
                        required:
                          - color
                          - image
                        properties:
                          color:
                            type: string
                            nullable: true
                            description: >-
                              CSS color string of the form background; null when
                              unset.
                          image:
                            type: object
                            nullable: true
                            required:
                              - url
                              - id
                            properties:
                              url:
                                type: string
                                format: uri
                              id:
                                type: string
                      primary_color:
                        type: string
                        nullable: true
                      submit_button:
                        type: object
                        required:
                          - style
                        properties:
                          style:
                            type: object
                            required:
                              - background_color
                              - color
                            properties:
                              background_color:
                                type: string
                                nullable: true
                              color:
                                type: string
                                nullable: true
                      typography:
                        type: object
                        required:
                          - form_header
                          - field_label
                          - choice_style
                        properties:
                          form_header:
                            type: object
                            required:
                              - font_size
                              - color
                              - font_weight
                              - text_align
                            properties:
                              font_size:
                                type: string
                                nullable: true
                                enum:
                                  - 14px
                                  - 15px
                                  - 16px
                                  - 20px
                                  - 24px
                                  - 28px
                                  - 36px
                                  - 40px
                                  - null
                              color:
                                type: string
                                nullable: true
                              font_weight:
                                type: string
                                nullable: true
                                enum:
                                  - bold
                                  - normal
                                  - null
                              text_align:
                                type: string
                                nullable: true
                                enum:
                                  - left
                                  - center
                                  - right
                                  - null
                          field_label:
                            type: object
                            required:
                              - font_size
                              - color
                              - font_weight
                            properties:
                              font_size:
                                type: string
                                nullable: true
                                enum:
                                  - 14px
                                  - 15px
                                  - 16px
                                  - 17px
                                  - 18px
                                  - 19px
                                  - 20px
                                  - 22px
                                  - 24px
                                  - null
                              color:
                                type: string
                                nullable: true
                              font_weight:
                                type: string
                                nullable: true
                                enum:
                                  - bold
                                  - normal
                                  - null
                          choice_style:
                            type: object
                            required:
                              - font_size
                              - color
                              - font_weight
                            properties:
                              font_size:
                                type: string
                                nullable: true
                                enum:
                                  - 14px
                                  - 15px
                                  - 16px
                                  - 17px
                                  - 18px
                                  - 19px
                                  - 20px
                                  - 22px
                                  - 24px
                                  - null
                              color:
                                type: string
                                nullable: true
                              font_weight:
                                type: string
                                nullable: true
                                enum:
                                  - bold
                                  - normal
                                  - null
                      general_button:
                        type: object
                        required:
                          - font_size
                          - color
                          - font_weight
                          - border_color
                          - border_width
                        properties:
                          font_size:
                            type: string
                            nullable: true
                            enum:
                              - 14px
                              - 15px
                              - 16px
                              - 17px
                              - 18px
                              - 19px
                              - 20px
                              - 22px
                              - 24px
                              - null
                          color:
                            type: string
                            nullable: true
                          font_weight:
                            type: string
                            nullable: true
                            enum:
                              - bold
                              - normal
                              - null
                          border_color:
                            type: string
                            nullable: true
                          border_width:
                            type: string
                            nullable: true
                            enum:
                              - 0px
                              - 1px
                              - 2px
                              - 3px
                              - 4px
                              - 5px
                              - null
                      form_container:
                        type: object
                        required:
                          - style
                        properties:
                          style:
                            type: object
                            required:
                              - background_color
                            properties:
                              background_color:
                                type: string
                                nullable: true
        '403':
          description: Wrong PAT scope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: Form not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: form_container rejected for non-classic layouts
          content:
            application/json:
              examples:
                conflict:
                  value:
                    error: >-
                      primary_color and submit_button.style.background_color
                      conflict
                mutex:
                  value:
                    error: header images and image_keywords are mutually exclusive
                unsplash_empty:
                  value:
                    error: Failed to fetch header image
                classic_layout:
                  value:
                    error: wallpaper image can only be set when layout is card
                card_layout:
                  value:
                    error: form_container can only be set when layout is classic
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - PersonalAccessToken:
            - form:write
components:
  schemas:
    FormThemeUpdateRequest:
      type: object
      description: >
        All fields optional; partial-merge semantics. Keys present in the body
        are

        written; keys absent are left unchanged. Explicit `null` clears that
        value.

        Header image(s) are set by providing **either** `header.images`
        (attachment ids)

        **or** `header.image_keywords` (Unsplash search).

        Pass `header.images: []` to remove all header images. A single-entry
        array

        configures a single header image; multiple entries configure a carousel.


        `primary_color` is an alias for `submit_button.style.background_color`;

        the two fields share storage and reject conflicting values.


        **Palette derivation.** When the header image actually changes — meaning

        the first image's attachment id differs from what was previously bound

        to the theme, or fresh bytes were downloaded — the server runs a palette

        derivation against the first image and applies the derived values

        (`wallpaper.color`, `submit_button.style.background_color`,

        `submit_button.style.color`, `typography.field_label.color`,

        `typography.choice_style.color`, `general_button.color`,

        `form_container.style.background_color`, plus the read-only

        `dominant_color`) to the theme.


        Each derived value **overwrites** the theme's current value unless the

        same PUT request explicitly sets that field — in which case the user's

        value wins (even when that value is `null`). For carousel headers

        (multiple images), only the first image is used as the derivation

        source; reordering or swapping later carousel items without changing

        the first image does not retrigger derivation. Pure id-reuse where the

        first image's id is unchanged triggers no recompute. `dominant_color`

        is always overwritten on image change; it cannot be set directly via

        PUT.


        The same derivation runs whether the new header image came from

        `header.images[].id` or `header.image_keywords` (Unsplash) —

        Unsplash photos are downloaded for analysis even though the served URL

        hotlinks the Unsplash CDN. `dominant_color` is always the
        locally-computed

        value from the downloaded bytes.
      additionalProperties: false
      properties:
        header:
          type: object
          additionalProperties: false
          properties:
            images:
              type: array
              maxItems: 10
              description: >-
                Header images. Empty array clears; one entry sets a single
                image; multiple entries configure a carousel. Each entry
                provides exactly one of `id` or `upload_id`.
              items:
                type: object
                additionalProperties: false
                oneOf:
                  - required:
                      - id
                  - required:
                      - upload_id
                description: >-
                  Provide exactly one of `id` (reuse an existing header image)
                  or `upload_id` (a 2-step upload prepared via POST
                  /api/v1/attachments).
                properties:
                  id:
                    type: string
                    minLength: 1
                    description: >-
                      Existing header image attachment id to reuse. Must be
                      non-empty.
                  upload_id:
                    type: string
                    minLength: 1
                    description: >-
                      upload_id from POST /api/v1/attachments (purpose
                      `header_image`), after the file was PUT to its upload_url.
                  url:
                    type: string
                    description: >-
                      Read-only; the image URL echoed by GET. Accepted and
                      ignored on write so a read payload can be sent back
                      verbatim.
            image_keywords:
              type: string
              description: >-
                Unsplash search keywords; the top result becomes the (sole)
                header image.
        wallpaper:
          type: object
          additionalProperties: false
          description: >-
            Form background. `color` and `image` are mutually exclusive —
            setting one clears the other. `image` is only accepted when the form
            layout is `card`. Setting either recomputes the palette (dominant +
            label/choice/header text colors).
          properties:
            color:
              type: string
              nullable: true
              description: CSS color string; null clears.
            image:
              nullable: true
              description: >-
                Card-only background image. Provide exactly one of `id` (reuse
                an existing wallpaper image) or `upload_id` (a 2-step upload
                prepared via POST /api/v1/attachments with purpose
                `wallpaper_image`). `null` clears the image.
              allOf:
                - type: object
                  additionalProperties: false
                  oneOf:
                    - required:
                        - id
                    - required:
                        - upload_id
                  properties:
                    id:
                      type: string
                      minLength: 1
                      description: Existing background image attachment id to reuse.
                    upload_id:
                      type: string
                      minLength: 1
                      description: >-
                        upload_id from POST /api/v1/attachments (purpose
                        `wallpaper_image`), after the file was PUT to its
                        upload_url.
                    url:
                      type: string
                      description: >-
                        Read-only; the image URL echoed by GET. Accepted and
                        ignored on write so a read payload can be sent back
                        verbatim.
        primary_color:
          type: string
          nullable: true
          description: >-
            Theme primary color. **Alias** for
            `submit_button.style.background_color` —

            reading either returns the same value, and writing to either updates
            the

            same underlying field. If both `primary_color` and

            `submit_button.style.background_color` are present in one request
            with

            **different** values, the request is rejected with 422. Equal values
            are

            accepted (idempotent).
        submit_button:
          type: object
          additionalProperties: false
          properties:
            style:
              type: object
              additionalProperties: false
              properties:
                background_color:
                  type: string
                  nullable: true
                color:
                  type: string
                  nullable: true
        typography:
          type: object
          additionalProperties: false
          properties:
            form_header:
              type: object
              additionalProperties: false
              properties:
                font_size:
                  type: string
                  nullable: true
                  enum:
                    - 14px
                    - 15px
                    - 16px
                    - 20px
                    - 24px
                    - 28px
                    - 36px
                    - 40px
                    - null
                  description: >-
                    Font size. One of 14px / 15px / 16px / 20px / 24px / 28px /
                    36px / 40px.
                color:
                  type: string
                  nullable: true
                  description: CSS color string.
                font_weight:
                  type: string
                  nullable: true
                  enum:
                    - bold
                    - normal
                    - null
                  description: Font weight; one of bold/normal.
                text_align:
                  type: string
                  nullable: true
                  enum:
                    - left
                    - center
                    - right
                    - null
                  description: Text alignment; one of left/center/right.
            field_label:
              type: object
              additionalProperties: false
              properties:
                font_size:
                  type: string
                  nullable: true
                  enum:
                    - 14px
                    - 15px
                    - 16px
                    - 17px
                    - 18px
                    - 19px
                    - 20px
                    - 22px
                    - 24px
                    - null
                  description: >-
                    Font size; one of
                    14px/15px/16px/17px/18px/19px/20px/22px/24px.
                color:
                  type: string
                  nullable: true
                  description: CSS color string.
                font_weight:
                  type: string
                  nullable: true
                  enum:
                    - bold
                    - normal
                    - null
                  description: Font weight; one of bold/normal.
            choice_style:
              type: object
              additionalProperties: false
              properties:
                font_size:
                  type: string
                  nullable: true
                  enum:
                    - 14px
                    - 15px
                    - 16px
                    - 17px
                    - 18px
                    - 19px
                    - 20px
                    - 22px
                    - 24px
                    - null
                  description: >-
                    Font size; one of
                    14px/15px/16px/17px/18px/19px/20px/22px/24px.
                color:
                  type: string
                  nullable: true
                  description: CSS color string.
                font_weight:
                  type: string
                  nullable: true
                  enum:
                    - bold
                    - normal
                    - null
                  description: Font weight; one of bold/normal.
        general_button:
          type: object
          additionalProperties: false
          properties:
            font_size:
              type: string
              nullable: true
              enum:
                - 14px
                - 15px
                - 16px
                - 17px
                - 18px
                - 19px
                - 20px
                - 22px
                - 24px
                - null
              description: Font size; one of 14px/15px/16px/17px/18px/19px/20px/22px/24px.
            color:
              type: string
              nullable: true
              description: CSS color string.
            font_weight:
              type: string
              nullable: true
              enum:
                - bold
                - normal
                - null
              description: Font weight; one of bold/normal.
            border_color:
              type: string
              nullable: true
              description: CSS color string.
            border_width:
              type: string
              nullable: true
              enum:
                - 0px
                - 1px
                - 2px
                - 3px
                - 4px
                - 5px
                - null
              description: Border width; one of 0px/1px/2px/3px/4px/5px.
        form_container:
          type: object
          additionalProperties: false
          description: >-
            Form container styling. Only accepted when the form layout is
            `classic`.
          properties:
            style:
              type: object
              additionalProperties: false
              properties:
                background_color:
                  type: string
                  nullable: true
                  description: CSS color string; null clears.
    ErrorEnvelope:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Error message
  securitySchemes:
    PersonalAccessToken:
      type: http
      scheme: bearer
      bearerFormat: PAT
      description: >
        Personal Access Token prefixed with `fh_`. Sent as `Authorization:
        Bearer fh_xxx`.

        The scope required by each endpoint is listed in that endpoint's
        description.

````