Skip to main content
PUT
/
api
/
v1
/
forms
/
{form_token}
/
theme
Update the form theme
curl --request PUT \
  --url https://formhug.ai/api/v1/forms/{form_token}/theme \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "header": {
    "images": [
      {
        "id": "<string>",
        "upload_id": "<string>",
        "url": "<string>"
      }
    ],
    "image_keywords": "<string>"
  },
  "wallpaper": {
    "color": "<string>",
    "image": {
      "id": "<string>",
      "upload_id": "<string>",
      "url": "<string>"
    }
  },
  "primary_color": "<string>",
  "submit_button": {
    "style": {
      "background_color": "<string>",
      "color": "<string>"
    }
  },
  "typography": {
    "form_header": {
      "color": "<string>"
    },
    "field_label": {
      "color": "<string>"
    },
    "choice_style": {
      "color": "<string>"
    }
  },
  "general_button": {
    "color": "<string>",
    "border_color": "<string>"
  },
  "form_container": {
    "style": {
      "background_color": "<string>"
    }
  }
}
'
import requests

url = "https://formhug.ai/api/v1/forms/{form_token}/theme"

payload = {
"header": {
"images": [
{
"id": "<string>",
"upload_id": "<string>",
"url": "<string>"
}
],
"image_keywords": "<string>"
},
"wallpaper": {
"color": "<string>",
"image": {
"id": "<string>",
"upload_id": "<string>",
"url": "<string>"
}
},
"primary_color": "<string>",
"submit_button": { "style": {
"background_color": "<string>",
"color": "<string>"
} },
"typography": {
"form_header": { "color": "<string>" },
"field_label": { "color": "<string>" },
"choice_style": { "color": "<string>" }
},
"general_button": {
"color": "<string>",
"border_color": "<string>"
},
"form_container": { "style": { "background_color": "<string>" } }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}

response = requests.put(url, json=payload, headers=headers)

print(response.text)
const options = {
method: 'PUT',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
header: {
images: [{id: '<string>', upload_id: '<string>', url: '<string>'}],
image_keywords: '<string>'
},
wallpaper: {
color: '<string>',
image: {id: '<string>', upload_id: '<string>', url: '<string>'}
},
primary_color: '<string>',
submit_button: {style: {background_color: '<string>', color: '<string>'}},
typography: {
form_header: {color: '<string>'},
field_label: {color: '<string>'},
choice_style: {color: '<string>'}
},
general_button: {color: '<string>', border_color: '<string>'},
form_container: {style: {background_color: '<string>'}}
})
};

fetch('https://formhug.ai/api/v1/forms/{form_token}/theme', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://formhug.ai/api/v1/forms/{form_token}/theme",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'header' => [
'images' => [
[
'id' => '<string>',
'upload_id' => '<string>',
'url' => '<string>'
]
],
'image_keywords' => '<string>'
],
'wallpaper' => [
'color' => '<string>',
'image' => [
'id' => '<string>',
'upload_id' => '<string>',
'url' => '<string>'
]
],
'primary_color' => '<string>',
'submit_button' => [
'style' => [
'background_color' => '<string>',
'color' => '<string>'
]
],
'typography' => [
'form_header' => [
'color' => '<string>'
],
'field_label' => [
'color' => '<string>'
],
'choice_style' => [
'color' => '<string>'
]
],
'general_button' => [
'color' => '<string>',
'border_color' => '<string>'
],
'form_container' => [
'style' => [
'background_color' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "https://formhug.ai/api/v1/forms/{form_token}/theme"

payload := strings.NewReader("{\n \"header\": {\n \"images\": [\n {\n \"id\": \"<string>\",\n \"upload_id\": \"<string>\",\n \"url\": \"<string>\"\n }\n ],\n \"image_keywords\": \"<string>\"\n },\n \"wallpaper\": {\n \"color\": \"<string>\",\n \"image\": {\n \"id\": \"<string>\",\n \"upload_id\": \"<string>\",\n \"url\": \"<string>\"\n }\n },\n \"primary_color\": \"<string>\",\n \"submit_button\": {\n \"style\": {\n \"background_color\": \"<string>\",\n \"color\": \"<string>\"\n }\n },\n \"typography\": {\n \"form_header\": {\n \"color\": \"<string>\"\n },\n \"field_label\": {\n \"color\": \"<string>\"\n },\n \"choice_style\": {\n \"color\": \"<string>\"\n }\n },\n \"general_button\": {\n \"color\": \"<string>\",\n \"border_color\": \"<string>\"\n },\n \"form_container\": {\n \"style\": {\n \"background_color\": \"<string>\"\n }\n }\n}")

req, _ := http.NewRequest("PUT", url, payload)

req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.put("https://formhug.ai/api/v1/forms/{form_token}/theme")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"header\": {\n \"images\": [\n {\n \"id\": \"<string>\",\n \"upload_id\": \"<string>\",\n \"url\": \"<string>\"\n }\n ],\n \"image_keywords\": \"<string>\"\n },\n \"wallpaper\": {\n \"color\": \"<string>\",\n \"image\": {\n \"id\": \"<string>\",\n \"upload_id\": \"<string>\",\n \"url\": \"<string>\"\n }\n },\n \"primary_color\": \"<string>\",\n \"submit_button\": {\n \"style\": {\n \"background_color\": \"<string>\",\n \"color\": \"<string>\"\n }\n },\n \"typography\": {\n \"form_header\": {\n \"color\": \"<string>\"\n },\n \"field_label\": {\n \"color\": \"<string>\"\n },\n \"choice_style\": {\n \"color\": \"<string>\"\n }\n },\n \"general_button\": {\n \"color\": \"<string>\",\n \"border_color\": \"<string>\"\n },\n \"form_container\": {\n \"style\": {\n \"background_color\": \"<string>\"\n }\n }\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://formhug.ai/api/v1/forms/{form_token}/theme")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"header\": {\n \"images\": [\n {\n \"id\": \"<string>\",\n \"upload_id\": \"<string>\",\n \"url\": \"<string>\"\n }\n ],\n \"image_keywords\": \"<string>\"\n },\n \"wallpaper\": {\n \"color\": \"<string>\",\n \"image\": {\n \"id\": \"<string>\",\n \"upload_id\": \"<string>\",\n \"url\": \"<string>\"\n }\n },\n \"primary_color\": \"<string>\",\n \"submit_button\": {\n \"style\": {\n \"background_color\": \"<string>\",\n \"color\": \"<string>\"\n }\n },\n \"typography\": {\n \"form_header\": {\n \"color\": \"<string>\"\n },\n \"field_label\": {\n \"color\": \"<string>\"\n },\n \"choice_style\": {\n \"color\": \"<string>\"\n }\n },\n \"general_button\": {\n \"color\": \"<string>\",\n \"border_color\": \"<string>\"\n },\n \"form_container\": {\n \"style\": {\n \"background_color\": \"<string>\"\n }\n }\n}"

response = http.request(request)
puts response.read_body
{
  "data": {
    "header": {
      "images": [
        {
          "url": "<string>",
          "id": "<string>"
        }
      ]
    },
    "wallpaper": {
      "color": "<string>",
      "image": {
        "url": "<string>",
        "id": "<string>"
      }
    },
    "primary_color": "<string>",
    "submit_button": {
      "style": {
        "background_color": "<string>",
        "color": "<string>"
      }
    },
    "typography": {
      "form_header": {
        "color": "<string>"
      },
      "field_label": {
        "color": "<string>"
      },
      "choice_style": {
        "color": "<string>"
      }
    },
    "general_button": {
      "color": "<string>",
      "border_color": "<string>"
    },
    "form_container": {
      "style": {
        "background_color": "<string>"
      }
    }
  }
}

Authorizations

Authorization
string
header
required

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.

Path Parameters

form_token
string
required

Form token

Body

application/json

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.

header
object
wallpaper
object

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

primary_color
string | null

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
object
typography
object
general_button
object
form_container
object

Form container styling. Only accepted when the form layout is classic.

Response

Null clears form_container.style.background_color

data
object
required
Last modified on July 9, 2026