Skip to content

Theme Customizations

Theme customizations are the configurable content blocks that drive the storefront home page and footer — image carousels, category carousels, product carousels, static-content slots, the footer-links columns, and the "services" / "USP" strip. Each block is scoped to a themeCode + channelId pair, with locale-specific options JSON inside its translations.

Endpoints

MethodPathPurpose
GET/api/shop/theme-customizationsPaginated list — supports ?type= filter
GET/api/shop/theme-customizations/{id}Single customization by ID

Use the example switcher above to flip between the unfiltered list, the ?type=footer_links filter, and a single fetch.

Request Headers

HeaderRequiredDescription
AcceptYesapplication/json
X-STOREFRONT-KEYYesStorefront API key (pk_storefront_…)
X-LocaleNoOverride request locale — affects which row populates translation. Default: channel locale.
X-ChannelNoOverride channel scope

Query Parameters (collection only)

ParameterTypeDefaultDescription
pageinteger1Page number (1-based)
per_pageinteger10Items per page. Max 100 for this endpoint.
typestringExact-match filter on the type field (see Supported types)

Pagination headers (X-Total-Count, X-Page, X-Per-Page, X-Total-Pages) are emitted on the collection. See Pagination.

Supported types

The type filter accepts any of these exact strings:

TypePurpose
image_carouselHero / banner image carousel on the home page
category_carouselHorizontally-scrolling list of categories
product_carouselHorizontally-scrolling list of products
static_contentFree-form HTML / Markdown block
footer_linksFooter link columns (privacy, terms, about, customer service, …)
services_content"Services" / USP strip (free shipping, 24×7 support, refund policy, …)

An unsupported value is accepted by the filter and returns an empty array rather than an error — the filter is not validated against the type list.

Customization Object Fields

Both endpoints return the same shape — the collection wraps an array of these objects, the single endpoint returns one.

FieldTypeDescription
idintegerCustomization primary key
themeCodestringTheme this block belongs to (e.g. default)
channelIdintegerChannel that owns this block — pair with themeCode
typestringOne of the supported types
namestringAdmin-facing name
sortOrderintegerDisplay order within the home page / footer
statusboolean (0/1)Whether the block is published — only published rows are returned
createdAt, updatedAtstring (ISO-8601)Timestamps
translationobject | nullInline translation for the request locale: { id, themeCustomizationId, locale, options }
translationsarray of objectsAll locale translations as inline objects (not IRI strings)

Both translation and translations[] are inlined — there are no IRIs to follow on this resource. The options field inside each translation is a JSON string, not a parsed object — you have to JSON.parse(translation.options) on the client.

Content is locale-specific, so pass X-Locale. The translation field returns the block's content for the requested locale, selected by the X-Locale request header (e.g. X-Locale: ar); omit it and you get the store default locale. The translations[] array always carries every locale, so you can also read them all and pick client-side.

Shape of the options payload (per type)

The options JSON string varies by type. Common shapes:

typeParsed options shape
image_carousel{ "images": [{ "image": "storage/theme/.../*.webp", "link": "...", "title": "..." }, …] }
product_carousel{ "title": "...", "filters": { "new": 1, "featured": 1, "limit": 10, "sort": "asc" } }filters is forwarded as query params to the Products listing
category_carousel{ "title": "...", "filters": { "parent_id": "1", "limit": "10", "sort": "asc" } }parent_id selects the parent whose children are shown (same value you'd pass to treeCategories(parentId:))
static_content{ "html": "<div>…</div>", "css": ".foo{…}" }
footer_links{ "column_1": [{ "url": "...", "title": "...", "sort_order": "3" }, …], "column_2": […], … }
services_content{ "services": [{ "service_icon": "storage/…", "title": "...", "description": "..." }, …] }

The client must parse that JSON string before using it — the value is stored and returned verbatim as text.

Images inside static_content need care: the stored html is authored for the storefront web theme, where <img> tags carry the real source in a data-src attribute (lazy-loading) and a placeholder in src. If you render this HTML in your own frontend, read data-src, not src, or images won't load.

Use Cases

  • Render the storefront home page in a single round trip: fetch the unfiltered collection, group by type, sort by sortOrder per group.
  • Render the footer in a focused request: ?type=footer_links returns just one row whose options carries the column data.
  • Build an admin preview of every locale: read all translations[] entries side-by-side.
  • Auto-detect new home-page blocks added by store admins by polling the collection — anything with status=1 will appear.

Best Practices

  • Parse options before use — it is a JSON string, not an object, on every translation.
  • Branch on type — four block types render straight from options, while product_carousel and category_carousel only carry filters you must then send to the product or category endpoints.
  • Send X-Localetranslation follows the request locale, and the default locale is what you get otherwise.
  • Read data-src, not src, inside static_content HTML — the markup is authored for lazy loading, so src holds a placeholder.

Released under the MIT License.