#Sites
GET /sites/{siteId}/brand-assets
List the images and files uploaded for a site's branding, along with how much of the storage allowance they use.
Requires the settings:read permission.
Path Parameters
siteIdstring (uuid)requiredThe ID of the site.
curl \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getSitesBySiteIdBrandAssets } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getSitesBySiteIdBrandAssets({
path: { siteId: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"url": "string",
"sizeBytes": 0,
"createdAt": "2025-01-15T09:30:00Z"
}
],
"totalSizeBytes": 0,
"limitBytes": 0
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the brand asset."
},
"name": {
"type": "string",
"description": "The original file name, unique within the site."
},
"url": {
"type": "string",
"description": "Public URL the asset is served from."
},
"sizeBytes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Size of the file in bytes, or null if the upload never completed."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the asset was added to the site."
}
},
"required": ["id", "name", "url", "sizeBytes", "createdAt"],
"additionalProperties": false
},
"description": "The site's brand assets, oldest first."
},
"totalSizeBytes": {
"type": "number",
"description": "Combined size of every brand asset on the site, in bytes."
},
"limitBytes": {
"type": "number",
"description": "How many bytes of brand assets the organization's plan allows per site."
}
},
"required": ["data", "totalSizeBytes", "limitBytes"],
"additionalProperties": false
}POST /sites/{siteId}/brand-assets
Attach an already-uploaded file to a site as a brand asset. Upload the file first with POST /files/upload-url, then pass the key it returns.
Requires the settings:write permission.
Path Parameters
siteIdstring (uuid)requiredThe ID of the site.
Body Parameters
keystringrequiredThe storage key returned by POST /files/upload-url for the uploaded file.
curl \
-X POST \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "string"
}'import { client } from "@cascade-commerce/api/admin/client"
import { postSitesBySiteIdBrandAssets } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postSitesBySiteIdBrandAssets({
path: { siteId: "550e8400-e29b-41d4-a716-446655440000" },
body: {
key: "string",
},
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"key": "string"
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"key": "string"
}"""
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"url": "string",
"sizeBytes": 0,
"createdAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the brand asset."
},
"name": {
"type": "string",
"description": "The original file name, unique within the site."
},
"url": {
"type": "string",
"description": "Public URL the asset is served from."
},
"sizeBytes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Size of the file in bytes, or null if the upload never completed."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the asset was added to the site."
}
},
"required": ["id", "name", "url", "sizeBytes", "createdAt"],
"additionalProperties": false,
"description": "The new brand asset."
}DELETE /sites/{siteId}/brand-assets/{fileId}
Delete a brand asset. Anything still pointing at its URL, such as an email template, will break.
Requires the settings:write permission.
Path Parameters
siteIdstring (uuid)requiredThe ID of the site the asset belongs to.
fileIdstring (uuid)requiredThe ID of the brand asset to delete.
curl \
-X DELETE \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { deleteSitesBySiteIdBrandAssetsByFileId } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await deleteSitesBySiteIdBrandAssetsByFileId({
path: {
siteId: "550e8400-e29b-41d4-a716-446655440000",
fileId: "550e8400-e29b-41d4-a716-446655440000",
},
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets/550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/brand-assets/550e8400-e29b-41d4-a716-446655440000",
method="DELETE",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"url": "string",
"sizeBytes": 0,
"createdAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the brand asset."
},
"name": {
"type": "string",
"description": "The original file name, unique within the site."
},
"url": {
"type": "string",
"description": "Public URL the asset is served from."
},
"sizeBytes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Size of the file in bytes, or null if the upload never completed."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the asset was added to the site."
}
},
"required": ["id", "name", "url", "sizeBytes", "createdAt"],
"additionalProperties": false,
"description": "The deleted brand asset."
}GET /sites
List the organization's sites, alphabetically by default. Supports filtering and cursor pagination.
Requires the settings:read permission.
Query Parameters
limitnumberThe number of records to return.
cursorstringA pagination cursor. Fetch the next page by passing the nextCursor value from the previous response.
sortname | -name | slug | -slug | createdAt | -createdAtThe sort order. Prefix a field with - to sort descending. Defaults to name.
filterstringA JSON-encoded filter document, for example {"createdAt":{"$gte":"2025-01-01T00:00:00Z"}}. Filterable fields: id, name, slug, baseUrl, createdAt. See Filtering for the syntax.
curl \
"https://api.cascade.dev/sites" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getSites } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getSites()require "net/http"
uri = URI("https://api.cascade.dev/sites")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/sites",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"slug": "string",
"baseUrl": "string",
"brandSettings": {
"primaryColor": "...",
"pageColor": "...",
"contentColor": "...",
"textColor": "...",
"mutedColor": "...",
"logoUrl": "...",
"iconUrl": "..."
},
"metadata": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}
],
"nextCursor": "string"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the site."
},
"name": {
"type": "string",
"description": "The site's display name."
},
"slug": {
"type": "string",
"description": "The site's short handle, unique within the organization. Used by imports and widgets."
},
"baseUrl": {
"type": "string",
"description": "The storefront's base URL, used to build links back to it."
},
"brandSettings": {
"type": "object",
"properties": {
"primaryColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Accent color used for buttons and links, as a CSS color string."
},
"pageColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Page background color, as a CSS color string."
},
"contentColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Background color for cards and panels, as a CSS color string."
},
"textColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Body text color, as a CSS color string."
},
"mutedColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Color for secondary and helper text, as a CSS color string."
},
"logoUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's logo image."
},
"iconUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's square icon."
}
},
"required": [
"primaryColor",
"pageColor",
"contentColor",
"textColor",
"mutedColor",
"logoUrl",
"iconUrl"
],
"additionalProperties": false,
"description": "Colors and imagery used to brand emails and customer-facing pages for this site."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was last changed."
}
},
"required": [
"id",
"name",
"slug",
"baseUrl",
"brandSettings",
"metadata",
"createdAt",
"updatedAt"
],
"additionalProperties": false
},
"description": "The page of records, in the requested sort order."
},
"nextCursor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Pass this back as `cursor` to fetch the next page. Null once the last page has been returned."
}
},
"required": ["data", "nextCursor"],
"additionalProperties": false
}POST /sites
Create a site. Fails if the organization is already at its plan's site limit, or if another site already uses the slug. Triggers the site.created webhook.
Requires the settings:write permission.
Body Parameters
dataobjectrequiredThe site to create.
curl \
-X POST \
"https://api.cascade.dev/sites" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"name": "string",
"slug": "string",
"baseUrl": "https://example.com",
"brandSettings": {
"logoKey": "string",
"iconKey": "string",
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string"
},
"metadata": "..."
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postSites } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postSites({
body: {
data: {
name: "string",
slug: "string",
baseUrl: "https://example.com",
brandSettings: {
logoKey: "string",
iconKey: "string",
primaryColor: "string",
pageColor: "string",
contentColor: "string",
textColor: "string",
mutedColor: "string",
},
metadata: "...",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/sites")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"name": "string",
"slug": "string",
"baseUrl": "https://example.com",
"brandSettings": {
"logoKey": "string",
"iconKey": "string",
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string"
},
"metadata": "..."
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"name": "string",
"slug": "string",
"baseUrl": "https://example.com",
"brandSettings": {
"logoKey": "string",
"iconKey": "string",
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string"
},
"metadata": "..."
}
}"""
req = Request(
"https://api.cascade.dev/sites",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"slug": "string",
"baseUrl": "string",
"brandSettings": {
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string",
"logoUrl": "https://example.com",
"iconUrl": "https://example.com"
},
"metadata": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the site."
},
"name": {
"type": "string",
"description": "The site's display name."
},
"slug": {
"type": "string",
"description": "The site's short handle, unique within the organization. Used by imports and widgets."
},
"baseUrl": {
"type": "string",
"description": "The storefront's base URL, used to build links back to it."
},
"brandSettings": {
"type": "object",
"properties": {
"primaryColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Accent color used for buttons and links, as a CSS color string."
},
"pageColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Page background color, as a CSS color string."
},
"contentColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Background color for cards and panels, as a CSS color string."
},
"textColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Body text color, as a CSS color string."
},
"mutedColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Color for secondary and helper text, as a CSS color string."
},
"logoUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's logo image."
},
"iconUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's square icon."
}
},
"required": [
"primaryColor",
"pageColor",
"contentColor",
"textColor",
"mutedColor",
"logoUrl",
"iconUrl"
],
"additionalProperties": false,
"description": "Colors and imagery used to brand emails and customer-facing pages for this site."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was last changed."
}
},
"required": [
"id",
"name",
"slug",
"baseUrl",
"brandSettings",
"metadata",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The new site."
}GET /sites/{id}
Get a single site by ID.
Requires the settings:read permission.
Path Parameters
idstring (uuid)requiredThe ID of the site.
curl \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getSitesById } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getSitesById({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"slug": "string",
"baseUrl": "string",
"brandSettings": {
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string",
"logoUrl": "https://example.com",
"iconUrl": "https://example.com"
},
"metadata": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the site."
},
"name": {
"type": "string",
"description": "The site's display name."
},
"slug": {
"type": "string",
"description": "The site's short handle, unique within the organization. Used by imports and widgets."
},
"baseUrl": {
"type": "string",
"description": "The storefront's base URL, used to build links back to it."
},
"brandSettings": {
"type": "object",
"properties": {
"primaryColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Accent color used for buttons and links, as a CSS color string."
},
"pageColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Page background color, as a CSS color string."
},
"contentColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Background color for cards and panels, as a CSS color string."
},
"textColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Body text color, as a CSS color string."
},
"mutedColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Color for secondary and helper text, as a CSS color string."
},
"logoUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's logo image."
},
"iconUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's square icon."
}
},
"required": [
"primaryColor",
"pageColor",
"contentColor",
"textColor",
"mutedColor",
"logoUrl",
"iconUrl"
],
"additionalProperties": false,
"description": "Colors and imagery used to brand emails and customer-facing pages for this site."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was last changed."
}
},
"required": [
"id",
"name",
"slug",
"baseUrl",
"brandSettings",
"metadata",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The requested site."
}PUT /sites/{id}
Update a site's details or branding. Fails if the new slug is already in use by another site. Triggers the site.updated webhook.
Requires the settings:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the site to update.
Body Parameters
dataobjectrequiredThe fields to update.
curl \
-X PUT \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"name": "string",
"slug": "string",
"baseUrl": "https://example.com",
"brandSettings": {
"logoKey": "string",
"iconKey": "string",
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string"
},
"metadata": "..."
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { putSitesById } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await putSitesById({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
body: {
data: {
name: "string",
slug: "string",
baseUrl: "https://example.com",
brandSettings: {
logoKey: "string",
iconKey: "string",
primaryColor: "string",
pageColor: "string",
contentColor: "string",
textColor: "string",
mutedColor: "string",
},
metadata: "...",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Put.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"name": "string",
"slug": "string",
"baseUrl": "https://example.com",
"brandSettings": {
"logoKey": "string",
"iconKey": "string",
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string"
},
"metadata": "..."
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"name": "string",
"slug": "string",
"baseUrl": "https://example.com",
"brandSettings": {
"logoKey": "string",
"iconKey": "string",
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string"
},
"metadata": "..."
}
}"""
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000",
data=body.encode(),
method="PUT",
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"slug": "string",
"baseUrl": "string",
"brandSettings": {
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string",
"logoUrl": "https://example.com",
"iconUrl": "https://example.com"
},
"metadata": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the site."
},
"name": {
"type": "string",
"description": "The site's display name."
},
"slug": {
"type": "string",
"description": "The site's short handle, unique within the organization. Used by imports and widgets."
},
"baseUrl": {
"type": "string",
"description": "The storefront's base URL, used to build links back to it."
},
"brandSettings": {
"type": "object",
"properties": {
"primaryColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Accent color used for buttons and links, as a CSS color string."
},
"pageColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Page background color, as a CSS color string."
},
"contentColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Background color for cards and panels, as a CSS color string."
},
"textColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Body text color, as a CSS color string."
},
"mutedColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Color for secondary and helper text, as a CSS color string."
},
"logoUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's logo image."
},
"iconUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's square icon."
}
},
"required": [
"primaryColor",
"pageColor",
"contentColor",
"textColor",
"mutedColor",
"logoUrl",
"iconUrl"
],
"additionalProperties": false,
"description": "Colors and imagery used to brand emails and customer-facing pages for this site."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was last changed."
}
},
"required": [
"id",
"name",
"slug",
"baseUrl",
"brandSettings",
"metadata",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The updated site."
}DELETE /sites/{id}
Delete a site. It is marked for deletion straight away and its records are cleared out in the background, so the site keeps showing up briefly. You cannot delete an organization's last site. Triggers the site.deleted webhook.
Requires the settings:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the site to delete.
curl \
-X DELETE \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { deleteSitesById } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await deleteSitesById({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000",
method="DELETE",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "string",
"slug": "string",
"baseUrl": "string",
"brandSettings": {
"primaryColor": "string",
"pageColor": "string",
"contentColor": "string",
"textColor": "string",
"mutedColor": "string",
"logoUrl": "https://example.com",
"iconUrl": "https://example.com"
},
"metadata": "...",
"createdAt": "2025-01-15T09:30:00Z",
"updatedAt": "2025-01-15T09:30:00Z"
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for the site."
},
"name": {
"type": "string",
"description": "The site's display name."
},
"slug": {
"type": "string",
"description": "The site's short handle, unique within the organization. Used by imports and widgets."
},
"baseUrl": {
"type": "string",
"description": "The storefront's base URL, used to build links back to it."
},
"brandSettings": {
"type": "object",
"properties": {
"primaryColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Accent color used for buttons and links, as a CSS color string."
},
"pageColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Page background color, as a CSS color string."
},
"contentColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Background color for cards and panels, as a CSS color string."
},
"textColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Body text color, as a CSS color string."
},
"mutedColor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Color for secondary and helper text, as a CSS color string."
},
"logoUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's logo image."
},
"iconUrl": {
"anyOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "null"
}
],
"description": "Public URL of the site's square icon."
}
},
"required": [
"primaryColor",
"pageColor",
"contentColor",
"textColor",
"mutedColor",
"logoUrl",
"iconUrl"
],
"additionalProperties": false,
"description": "Colors and imagery used to brand emails and customer-facing pages for this site."
},
"metadata": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
},
"description": "Free-form string key/value pairs for your own data. Individual keys are filterable with a dot path, for example `metadata.category`."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was created."
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When the site was last changed."
}
},
"required": [
"id",
"name",
"slug",
"baseUrl",
"brandSettings",
"metadata",
"createdAt",
"updatedAt"
],
"additionalProperties": false,
"description": "The site now marked for deletion."
}GET /sites/{id}/widget-theme
Get how the storefront widgets look on one site: every property with the defaults filled in, the subset this site has actually changed, and the URL the widgets load the stylesheet from.
Requires the settings:read permission.
Path Parameters
idstring (uuid)requiredThe ID of the site.
curl \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getSitesByIdWidgetTheme } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getSitesByIdWidgetTheme({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"siteId": "550e8400-e29b-41d4-a716-446655440000",
"theme": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
},
"overrides": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
},
"stylesheetUrl": "string",
"notes": ["string"]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"siteId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The site this appearance belongs to."
},
"theme": {
"type": "object",
"properties": {
"fontFamily": {
"type": "string",
"description": "The font stack the widgets use, as a CSS `font-family` value. `inherit` takes the storefront's own face."
},
"text": {
"type": "string",
"description": "Body text color, as a CSS color. `inherit` takes the color of whatever the widget sits in."
},
"mutedText": {
"type": "string",
"description": "Color for secondary text: dates, counts, help lines."
},
"surface": {
"type": "string",
"description": "The widgets' own background, as a CSS color. `transparent` lets the storefront's page show through."
},
"surfaceMuted": {
"type": "string",
"description": "Quiet fill behind rating bars, thumbnails and the summary panel."
},
"border": {
"type": "string",
"description": "Color of the hairlines between rows, cards and controls."
},
"accent": {
"type": "string",
"description": "Background of the filled buttons, the active tab and progress fills."
},
"accentText": {
"type": "string",
"description": "Text and glyph color on top of the accent color."
},
"star": {
"type": "string",
"description": "Fill of a rated star and of the rating breakdown bars."
},
"danger": {
"type": "string",
"description": "Color of error messages and of a negative review theme."
},
"radius": {
"type": "string",
"description": "Corner radius of cards, panels and controls, as a CSS length such as `8px`."
},
"customCss": {
"type": "string",
"description": "Extra CSS for these widgets. It is sanitized and scoped to the widget root before it is served, so it cannot restyle the rest of the page."
}
},
"required": [
"fontFamily",
"text",
"mutedText",
"surface",
"surfaceMuted",
"border",
"accent",
"accentText",
"star",
"danger",
"radius",
"customCss"
],
"additionalProperties": false,
"description": "The resolved appearance: every property, with the defaults filled in where the site has not set one."
},
"overrides": {
"type": "object",
"properties": {
"fontFamily": {
"type": "string",
"description": "The font stack the widgets use, as a CSS `font-family` value. `inherit` takes the storefront's own face."
},
"text": {
"type": "string",
"description": "Body text color, as a CSS color. `inherit` takes the color of whatever the widget sits in."
},
"mutedText": {
"type": "string",
"description": "Color for secondary text: dates, counts, help lines."
},
"surface": {
"type": "string",
"description": "The widgets' own background, as a CSS color. `transparent` lets the storefront's page show through."
},
"surfaceMuted": {
"type": "string",
"description": "Quiet fill behind rating bars, thumbnails and the summary panel."
},
"border": {
"type": "string",
"description": "Color of the hairlines between rows, cards and controls."
},
"accent": {
"type": "string",
"description": "Background of the filled buttons, the active tab and progress fills."
},
"accentText": {
"type": "string",
"description": "Text and glyph color on top of the accent color."
},
"star": {
"type": "string",
"description": "Fill of a rated star and of the rating breakdown bars."
},
"danger": {
"type": "string",
"description": "Color of error messages and of a negative review theme."
},
"radius": {
"type": "string",
"description": "Corner radius of cards, panels and controls, as a CSS length such as `8px`."
},
"customCss": {
"type": "string",
"description": "Extra CSS for these widgets. It is sanitized and scoped to the widget root before it is served, so it cannot restyle the rest of the page."
}
},
"additionalProperties": false,
"description": "Only the properties this site has actually changed."
},
"stylesheetUrl": {
"type": "string",
"description": "The path the widgets load this appearance from. The version in it changes whenever the appearance does, so the file can be cached indefinitely."
},
"notes": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the sanitizer took out of the custom CSS you sent, so a dropped rule is never a silent one. Empty on a read."
}
},
"required": ["siteId", "theme", "overrides", "stylesheetUrl", "notes"],
"additionalProperties": false,
"description": "The site's widget appearance."
}PATCH /sites/{id}/widget-theme
Change how the widgets look on one site. Only the properties you send are changed. Custom CSS is sanitized before it is stored: at-rules other than @media, @supports and @container are dropped, url() may only carry a data: URI, and every selector is scoped to the widget root so it cannot reach the rest of the page. Saving custom CSS also records a version you can restore.
Requires the settings:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the site.
Body Parameters
dataobjectrequiredThe properties to change. Anything you leave out keeps its current value, and setting a property to its default clears the override.
curl \
-X PATCH \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { patchSitesByIdWidgetTheme } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await patchSitesByIdWidgetTheme({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
body: {
data: {
fontFamily: "string",
text: "string",
mutedText: "string",
surface: "string",
surfaceMuted: "string",
border: "string",
accent: "string",
accentText: "string",
star: "string",
danger: "string",
radius: "string",
customCss: "string",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme")
req = Net::HTTP::Patch.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
}
}"""
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme",
data=body.encode(),
method="PATCH",
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"siteId": "550e8400-e29b-41d4-a716-446655440000",
"theme": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
},
"overrides": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
},
"stylesheetUrl": "string",
"notes": ["string"]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"siteId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The site this appearance belongs to."
},
"theme": {
"type": "object",
"properties": {
"fontFamily": {
"type": "string",
"description": "The font stack the widgets use, as a CSS `font-family` value. `inherit` takes the storefront's own face."
},
"text": {
"type": "string",
"description": "Body text color, as a CSS color. `inherit` takes the color of whatever the widget sits in."
},
"mutedText": {
"type": "string",
"description": "Color for secondary text: dates, counts, help lines."
},
"surface": {
"type": "string",
"description": "The widgets' own background, as a CSS color. `transparent` lets the storefront's page show through."
},
"surfaceMuted": {
"type": "string",
"description": "Quiet fill behind rating bars, thumbnails and the summary panel."
},
"border": {
"type": "string",
"description": "Color of the hairlines between rows, cards and controls."
},
"accent": {
"type": "string",
"description": "Background of the filled buttons, the active tab and progress fills."
},
"accentText": {
"type": "string",
"description": "Text and glyph color on top of the accent color."
},
"star": {
"type": "string",
"description": "Fill of a rated star and of the rating breakdown bars."
},
"danger": {
"type": "string",
"description": "Color of error messages and of a negative review theme."
},
"radius": {
"type": "string",
"description": "Corner radius of cards, panels and controls, as a CSS length such as `8px`."
},
"customCss": {
"type": "string",
"description": "Extra CSS for these widgets. It is sanitized and scoped to the widget root before it is served, so it cannot restyle the rest of the page."
}
},
"required": [
"fontFamily",
"text",
"mutedText",
"surface",
"surfaceMuted",
"border",
"accent",
"accentText",
"star",
"danger",
"radius",
"customCss"
],
"additionalProperties": false,
"description": "The resolved appearance: every property, with the defaults filled in where the site has not set one."
},
"overrides": {
"type": "object",
"properties": {
"fontFamily": {
"type": "string",
"description": "The font stack the widgets use, as a CSS `font-family` value. `inherit` takes the storefront's own face."
},
"text": {
"type": "string",
"description": "Body text color, as a CSS color. `inherit` takes the color of whatever the widget sits in."
},
"mutedText": {
"type": "string",
"description": "Color for secondary text: dates, counts, help lines."
},
"surface": {
"type": "string",
"description": "The widgets' own background, as a CSS color. `transparent` lets the storefront's page show through."
},
"surfaceMuted": {
"type": "string",
"description": "Quiet fill behind rating bars, thumbnails and the summary panel."
},
"border": {
"type": "string",
"description": "Color of the hairlines between rows, cards and controls."
},
"accent": {
"type": "string",
"description": "Background of the filled buttons, the active tab and progress fills."
},
"accentText": {
"type": "string",
"description": "Text and glyph color on top of the accent color."
},
"star": {
"type": "string",
"description": "Fill of a rated star and of the rating breakdown bars."
},
"danger": {
"type": "string",
"description": "Color of error messages and of a negative review theme."
},
"radius": {
"type": "string",
"description": "Corner radius of cards, panels and controls, as a CSS length such as `8px`."
},
"customCss": {
"type": "string",
"description": "Extra CSS for these widgets. It is sanitized and scoped to the widget root before it is served, so it cannot restyle the rest of the page."
}
},
"additionalProperties": false,
"description": "Only the properties this site has actually changed."
},
"stylesheetUrl": {
"type": "string",
"description": "The path the widgets load this appearance from. The version in it changes whenever the appearance does, so the file can be cached indefinitely."
},
"notes": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the sanitizer took out of the custom CSS you sent, so a dropped rule is never a silent one. Empty on a read."
}
},
"required": ["siteId", "theme", "overrides", "stylesheetUrl", "notes"],
"additionalProperties": false,
"description": "The site's appearance after the change."
}POST /sites/{id}/widget-theme/copy
Copy another site's widget appearance onto this one, custom CSS included. It is a copy, not a link: the two drift apart as soon as either is edited again.
Requires the settings:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the site.
Body Parameters
dataobjectrequiredcurl \
-X POST \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/copy" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"fromSiteId": "550e8400-e29b-41d4-a716-446655440000"
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postSitesByIdWidgetThemeCopy } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postSitesByIdWidgetThemeCopy({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
body: {
data: {
fromSiteId: "550e8400-e29b-41d4-a716-446655440000",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/copy")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"fromSiteId": "550e8400-e29b-41d4-a716-446655440000"
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"fromSiteId": "550e8400-e29b-41d4-a716-446655440000"
}
}"""
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/copy",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"siteId": "550e8400-e29b-41d4-a716-446655440000",
"theme": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
},
"overrides": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
},
"stylesheetUrl": "string",
"notes": ["string"]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"siteId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The site this appearance belongs to."
},
"theme": {
"type": "object",
"properties": {
"fontFamily": {
"type": "string",
"description": "The font stack the widgets use, as a CSS `font-family` value. `inherit` takes the storefront's own face."
},
"text": {
"type": "string",
"description": "Body text color, as a CSS color. `inherit` takes the color of whatever the widget sits in."
},
"mutedText": {
"type": "string",
"description": "Color for secondary text: dates, counts, help lines."
},
"surface": {
"type": "string",
"description": "The widgets' own background, as a CSS color. `transparent` lets the storefront's page show through."
},
"surfaceMuted": {
"type": "string",
"description": "Quiet fill behind rating bars, thumbnails and the summary panel."
},
"border": {
"type": "string",
"description": "Color of the hairlines between rows, cards and controls."
},
"accent": {
"type": "string",
"description": "Background of the filled buttons, the active tab and progress fills."
},
"accentText": {
"type": "string",
"description": "Text and glyph color on top of the accent color."
},
"star": {
"type": "string",
"description": "Fill of a rated star and of the rating breakdown bars."
},
"danger": {
"type": "string",
"description": "Color of error messages and of a negative review theme."
},
"radius": {
"type": "string",
"description": "Corner radius of cards, panels and controls, as a CSS length such as `8px`."
},
"customCss": {
"type": "string",
"description": "Extra CSS for these widgets. It is sanitized and scoped to the widget root before it is served, so it cannot restyle the rest of the page."
}
},
"required": [
"fontFamily",
"text",
"mutedText",
"surface",
"surfaceMuted",
"border",
"accent",
"accentText",
"star",
"danger",
"radius",
"customCss"
],
"additionalProperties": false,
"description": "The resolved appearance: every property, with the defaults filled in where the site has not set one."
},
"overrides": {
"type": "object",
"properties": {
"fontFamily": {
"type": "string",
"description": "The font stack the widgets use, as a CSS `font-family` value. `inherit` takes the storefront's own face."
},
"text": {
"type": "string",
"description": "Body text color, as a CSS color. `inherit` takes the color of whatever the widget sits in."
},
"mutedText": {
"type": "string",
"description": "Color for secondary text: dates, counts, help lines."
},
"surface": {
"type": "string",
"description": "The widgets' own background, as a CSS color. `transparent` lets the storefront's page show through."
},
"surfaceMuted": {
"type": "string",
"description": "Quiet fill behind rating bars, thumbnails and the summary panel."
},
"border": {
"type": "string",
"description": "Color of the hairlines between rows, cards and controls."
},
"accent": {
"type": "string",
"description": "Background of the filled buttons, the active tab and progress fills."
},
"accentText": {
"type": "string",
"description": "Text and glyph color on top of the accent color."
},
"star": {
"type": "string",
"description": "Fill of a rated star and of the rating breakdown bars."
},
"danger": {
"type": "string",
"description": "Color of error messages and of a negative review theme."
},
"radius": {
"type": "string",
"description": "Corner radius of cards, panels and controls, as a CSS length such as `8px`."
},
"customCss": {
"type": "string",
"description": "Extra CSS for these widgets. It is sanitized and scoped to the widget root before it is served, so it cannot restyle the rest of the page."
}
},
"additionalProperties": false,
"description": "Only the properties this site has actually changed."
},
"stylesheetUrl": {
"type": "string",
"description": "The path the widgets load this appearance from. The version in it changes whenever the appearance does, so the file can be cached indefinitely."
},
"notes": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the sanitizer took out of the custom CSS you sent, so a dropped rule is never a silent one. Empty on a read."
}
},
"required": ["siteId", "theme", "overrides", "stylesheetUrl", "notes"],
"additionalProperties": false,
"description": "The site's appearance after the copy."
}GET /sites/{id}/widget-theme/revisions
List the saved versions of this site's custom CSS, newest first. A version is recorded every time the CSS changes, whoever or whatever changed it.
Requires the settings:read permission.
Path Parameters
idstring (uuid)requiredThe ID of the site.
curl \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/revisions" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { getSitesByIdWidgetThemeRevisions } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await getSitesByIdWidgetThemeRevisions({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/revisions")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/revisions",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"customCss": "string",
"source": "manual",
"prompt": "string",
"actorLabel": "string",
"createdAt": "2025-01-15T09:30:00Z"
}
]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Unique identifier for this saved version."
},
"customCss": {
"type": "string",
"description": "The custom CSS as it was served by this version."
},
"source": {
"type": "string",
"enum": ["manual", "ai", "restore"],
"description": "Whether the custom CSS was typed, generated from a prompt, or put back from history."
},
"prompt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "What the merchant asked a model for, on a generated version only."
},
"actorLabel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who saved it, where that is known."
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
"description": "When it was saved."
}
},
"required": ["id", "customCss", "source", "prompt", "actorLabel", "createdAt"],
"additionalProperties": false
},
"description": "The saved versions, newest first."
}
},
"required": ["data"],
"additionalProperties": false
}POST /sites/{id}/widget-theme/revisions/{revisionId}/restore
Put a saved version of the custom CSS back. The restore is itself recorded as a version, so nothing is lost by undoing an undo.
Requires the settings:write permission.
Path Parameters
idstring (uuid)requiredThe ID of the site.
revisionIdstring (uuid)requiredThe ID of the version to restore.
curl \
-X POST \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/revisions/550e8400-e29b-41d4-a716-446655440000/restore" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY"import { client } from "@cascade-commerce/api/admin/client"
import { postSitesByIdWidgetThemeRevisionsByRevisionIdRestore } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postSitesByIdWidgetThemeRevisionsByRevisionIdRestore({
path: {
id: "550e8400-e29b-41d4-a716-446655440000",
revisionId: "550e8400-e29b-41d4-a716-446655440000",
},
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/revisions/550e8400-e29b-41d4-a716-446655440000/restore")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/revisions/550e8400-e29b-41d4-a716-446655440000/restore",
method="POST",
headers={"Authorization": "Bearer sk_YOUR_SECRET_KEY"},
)
with urlopen(req) as res:
print(json.load(res)){
"siteId": "550e8400-e29b-41d4-a716-446655440000",
"theme": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
},
"overrides": {
"fontFamily": "string",
"text": "string",
"mutedText": "string",
"surface": "string",
"surfaceMuted": "string",
"border": "string",
"accent": "string",
"accentText": "string",
"star": "string",
"danger": "string",
"radius": "string",
"customCss": "string"
},
"stylesheetUrl": "string",
"notes": ["string"]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"siteId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The site this appearance belongs to."
},
"theme": {
"type": "object",
"properties": {
"fontFamily": {
"type": "string",
"description": "The font stack the widgets use, as a CSS `font-family` value. `inherit` takes the storefront's own face."
},
"text": {
"type": "string",
"description": "Body text color, as a CSS color. `inherit` takes the color of whatever the widget sits in."
},
"mutedText": {
"type": "string",
"description": "Color for secondary text: dates, counts, help lines."
},
"surface": {
"type": "string",
"description": "The widgets' own background, as a CSS color. `transparent` lets the storefront's page show through."
},
"surfaceMuted": {
"type": "string",
"description": "Quiet fill behind rating bars, thumbnails and the summary panel."
},
"border": {
"type": "string",
"description": "Color of the hairlines between rows, cards and controls."
},
"accent": {
"type": "string",
"description": "Background of the filled buttons, the active tab and progress fills."
},
"accentText": {
"type": "string",
"description": "Text and glyph color on top of the accent color."
},
"star": {
"type": "string",
"description": "Fill of a rated star and of the rating breakdown bars."
},
"danger": {
"type": "string",
"description": "Color of error messages and of a negative review theme."
},
"radius": {
"type": "string",
"description": "Corner radius of cards, panels and controls, as a CSS length such as `8px`."
},
"customCss": {
"type": "string",
"description": "Extra CSS for these widgets. It is sanitized and scoped to the widget root before it is served, so it cannot restyle the rest of the page."
}
},
"required": [
"fontFamily",
"text",
"mutedText",
"surface",
"surfaceMuted",
"border",
"accent",
"accentText",
"star",
"danger",
"radius",
"customCss"
],
"additionalProperties": false,
"description": "The resolved appearance: every property, with the defaults filled in where the site has not set one."
},
"overrides": {
"type": "object",
"properties": {
"fontFamily": {
"type": "string",
"description": "The font stack the widgets use, as a CSS `font-family` value. `inherit` takes the storefront's own face."
},
"text": {
"type": "string",
"description": "Body text color, as a CSS color. `inherit` takes the color of whatever the widget sits in."
},
"mutedText": {
"type": "string",
"description": "Color for secondary text: dates, counts, help lines."
},
"surface": {
"type": "string",
"description": "The widgets' own background, as a CSS color. `transparent` lets the storefront's page show through."
},
"surfaceMuted": {
"type": "string",
"description": "Quiet fill behind rating bars, thumbnails and the summary panel."
},
"border": {
"type": "string",
"description": "Color of the hairlines between rows, cards and controls."
},
"accent": {
"type": "string",
"description": "Background of the filled buttons, the active tab and progress fills."
},
"accentText": {
"type": "string",
"description": "Text and glyph color on top of the accent color."
},
"star": {
"type": "string",
"description": "Fill of a rated star and of the rating breakdown bars."
},
"danger": {
"type": "string",
"description": "Color of error messages and of a negative review theme."
},
"radius": {
"type": "string",
"description": "Corner radius of cards, panels and controls, as a CSS length such as `8px`."
},
"customCss": {
"type": "string",
"description": "Extra CSS for these widgets. It is sanitized and scoped to the widget root before it is served, so it cannot restyle the rest of the page."
}
},
"additionalProperties": false,
"description": "Only the properties this site has actually changed."
},
"stylesheetUrl": {
"type": "string",
"description": "The path the widgets load this appearance from. The version in it changes whenever the appearance does, so the file can be cached indefinitely."
},
"notes": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the sanitizer took out of the custom CSS you sent, so a dropped rule is never a silent one. Empty on a read."
}
},
"required": ["siteId", "theme", "overrides", "stylesheetUrl", "notes"],
"additionalProperties": false,
"description": "The site's appearance after the restore."
}POST /sites/{id}/widget-theme/suggest-css
Ask a model to write custom CSS for these widgets from a description. Nothing is saved: what comes back is already sanitized and scoped, so it is exactly what would be served, and you store it by sending it to PATCH /sites/{id}/widget-theme. Limited to 20 requests per organization, refilling at one a minute.
Requires the settings:write permission.
Costs 5 rate limit tokens instead of the usual one. See Rate limits.
Path Parameters
idstring (uuid)requiredThe ID of the site.
Body Parameters
dataobjectrequiredcurl \
-X POST \
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/suggest-css" \
-H "Authorization: Bearer sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"prompt": "string"
}
}'import { client } from "@cascade-commerce/api/admin/client"
import { postSitesByIdWidgetThemeSuggestCss } from "@cascade-commerce/api/admin"
client.setConfig({
baseUrl: "https://api.cascade.dev",
headers: { Authorization: "Bearer sk_YOUR_SECRET_KEY" },
})
const { data, error } = await postSitesByIdWidgetThemeSuggestCss({
path: { id: "550e8400-e29b-41d4-a716-446655440000" },
body: {
data: {
prompt: "string",
},
},
})require "net/http"
uri = URI("https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/suggest-css")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer sk_YOUR_SECRET_KEY"
req["Content-Type"] = "application/json"
req.body = <<~JSON
{
"data": {
"prompt": "string"
}
}
JSON
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.bodyimport json
from urllib.request import Request, urlopen
body = """{
"data": {
"prompt": "string"
}
}"""
req = Request(
"https://api.cascade.dev/sites/550e8400-e29b-41d4-a716-446655440000/widget-theme/suggest-css",
data=body.encode(),
headers={
"Authorization": "Bearer sk_YOUR_SECRET_KEY",
"Content-Type": "application/json",
},
)
with urlopen(req) as res:
print(json.load(res)){
"css": "string",
"summary": "string",
"notes": ["string"]
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"css": {
"type": "string",
"description": "The suggested custom CSS, already sanitized and scoped, so it is exactly what would be served if you accept it. Nothing is saved until you send it back as an update."
},
"summary": {
"type": "string",
"description": "A sentence or two on what the CSS changes."
},
"notes": {
"type": "array",
"items": {
"type": "string"
},
"description": "Anything the sanitizer removed from the model's answer before returning it."
}
},
"required": ["css", "summary", "notes"],
"additionalProperties": false,
"description": "The suggested CSS."
}