Skip to main content
POST
/
v1
/
themes
/
{themeId}
Update a theme
curl --request POST \
  --url https://app.loops.so/api/v1/themes/{themeId} \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "name": "<string>",
  "styles": {}
}
'
import requests

url = "https://app.loops.so/api/v1/themes/{themeId}"

payload = {
"name": "<string>",
"styles": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}

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

print(response.text)
const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({name: '<string>', styles: {}})
};

fetch('https://app.loops.so/api/v1/themes/{themeId}', 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://app.loops.so/api/v1/themes/{themeId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'styles' => [

]
]),
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://app.loops.so/api/v1/themes/{themeId}"

payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"styles\": {}\n}")

req, _ := http.NewRequest("POST", 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.post("https://app.loops.so/api/v1/themes/{themeId}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"styles\": {}\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://app.loops.so/api/v1/themes/{themeId}")

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

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"styles\": {}\n}"

response = http.request(request)
puts response.read_body
{
  "id": "clo1z5q7s004yl70y3z4a5b6c",
  "name": "Marketing default",
  "styles": {
    "backgroundColor": "#ffffff",
    "textBaseColor": "#111111",
    "textBaseFontSize": 16
  },
  "isDefault": true,
  "createdAt": "2026-03-28T15:00:00.000Z",
  "updatedAt": "2026-03-28T15:30:00.000Z",
  "affectedEmailCount": 12
}
{
  "message": "Theme not found."
}
At least one of name or styles must be provided.
When styles change, the update cascades to every email using this theme. affectedEmailCount in the response reports how many emails were affected. Manual style edits made on individual emails are preserved: the cascade only changes properties an email has not overridden. A per-email override is removed only when it becomes identical to the theme’s new value, after which that email follows the theme for that property.

Request

Path parameters

themeId
string
required
The ID of the theme.

Body

name
string
The theme name.
styles
object
Style attributes for the theme. Attributes use the same names as the LMX <Style /> tag attributes.

Response

Success

id
string
required
The theme ID.
name
string
required
The theme name.
styles
object
required
The theme’s style attributes.
isDefault
boolean
required
Whether this theme is the team’s default theme.
createdAt
string
required
ISO 8601 timestamp for when the theme was created.
updatedAt
string
required
ISO 8601 timestamp for when the theme was last updated.
affectedEmailCount
number
required
The number of emails using this theme that are affected by the style change. 0 when only the name changed.

Error

If the request body is invalid, a 400 Bad Request is returned. A 404 Not Found is returned if the theme does not exist. If the API key is invalid (or content API is not enabled for your team), a 401 Unauthorized is returned.
message
string
required
An error message describing what went wrong.
{
  "id": "clo1z5q7s004yl70y3z4a5b6c",
  "name": "Marketing default",
  "styles": {
    "backgroundColor": "#ffffff",
    "textBaseColor": "#111111",
    "textBaseFontSize": 16
  },
  "isDefault": true,
  "createdAt": "2026-03-28T15:00:00.000Z",
  "updatedAt": "2026-03-28T15:30:00.000Z",
  "affectedEmailCount": 12
}
{
  "message": "Theme not found."
}
Last modified on July 9, 2026