Skip to main content

Widget API

Widgets are configurable UI components rendered into storefront areas. A widget is stored as two things:

  • a widget instance (widget_instance) — the type, name, status, theme and settings;
  • zero or more placements (widget_placement) — one row per (route, area) cell, each with its own sort_order.

That separation is the main change to be aware of: area, route and sort_order are no longer columns on the widget itself. The primary key column is widget_instance_id, and the settings column is settings (not setting).

Endpoints​

Create a Widget​

Creates a widget instance and its placements.

Request Schema (application/json)
Field NameField TypeRequired
typestringNo
namestringYes
statusstring or integer (0, 1, '0', '1')Yes
settingsobjectNo
placementsarray of objectNo
Field NameField TypeRequired
routestringNo
areastringNo
sort_ordernumberNo
routearray of stringNo
areaarray of stringNo
sort_orderstring or integerNo
POST/api/widgets
curl
-H "Accept: application/json"
-H "Authorization: Bearer <admin JWT token>"
--data-raw '<JSON DATA>'
https://<your domain>/api/widgets
Sample Of Response
{
"data": {
"widget_instance_id": 5,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "collection_products",
"name": "Featured Products",
"status": 1,
"theme": "eve",
"settings": {
"collection": "summer-sale",
"count": 4
},
"links": [
{
"rel": "widgetGrid",
"href": "/admin/widgets",
"action": "GET",
"types": ["text/xml"]
},
{
"rel": "edit",
"href": "/admin/widgets/edit/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"action": "GET",
"types": ["text/xml"]
}
]
}
}

A create payload using the preferred placement shape:

{
"type": "collection_products",
"name": "Featured Products",
"status": 1,
"settings": {
"collection": "summer-sale",
"count": 4
},
"placements": [
{ "route": "homepage", "area": "content", "sort_order": 10 },
{ "route": "categoryView", "area": "beforeFooter", "sort_order": 20 }
]
}

Placement entries missing route or area are skipped silently. If you send neither placements nor route/area, the widget is created with zero placements and renders nowhere.


Update a Widget​

Updates the instance and, when the payload touches placement fields, rebuilds its placements.

Request Schema (application/json)
Field NameField TypeRequired
namestringNo
statusstring or integer (0, 1, '0', '1')Yes
settingsobjectNo
placementsarray of objectNo
Field NameField TypeRequired
routestringNo
areastringNo
sort_ordernumberNo
routearray of stringNo
areaarray of stringNo
sort_orderstring or integerNo
PATCH/api/widgets/{id}
curl
-H "Accept: application/json"
-H "Authorization: Bearer <admin JWT token>"
--data-raw '<JSON DATA>'
https://<your domain>/api/widgets/{id}
Sample Of Response
{
"data": {
"widget_instance_id": 5,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Featured Products Updated",
"status": 1,
"links": [
{
"rel": "widgetGrid",
"href": "/admin/widgets",
"action": "GET",
"types": ["text/xml"]
},
{
"rel": "edit",
"href": "/admin/widgets/edit/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"action": "GET",
"types": ["text/xml"]
}
]
}
}

Placement replacement rules:

PayloadEffect on placements
placements presentRoute-level placements are deleted and recreated from the list. Entity-scoped placements (the ones the page builder owns) are left untouched
any of route, area, sort_order presentAll placements are deleted and recreated from the cross-product of route x area
none of the abovePlacements are left untouched

Delete a Widget​

Permanently removes a widget instance. Its placements cascade.

Request Schema (application/json)
No request body required
DELETE/api/widgets/{id}
curl
-H "Accept: application/json"
-H "Authorization: Bearer <admin JWT token>"
https://<your domain>/api/widgets/{id}
Sample Of Response
{
"data": {
"widget_instance_id": 5,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}

Notes​

{id} is the instance uuid​

Every /api/widgets/{id} endpoint resolves the widget by widget_instance.uuid. A well-formed but unknown uuid returns Requested widget not found on PATCH and Invalid widget id on DELETE. Passing the integer widget_instance_id is not a "not found" case at all: uuid is a Postgres uuid column, so the comparison raises invalid input syntax for type uuid and both endpoints surface it as a 500.

theme is stamped on create only​

On create, the widget's theme is stamped from the active theme after validation, so new widgets land in the current theme's bucket.

On update it is not re-stamped. updateWidget passes the payload straight to .given(data), so a theme value in the PATCH body is written through to the row and can move the widget into another theme's bucket — where the storefront's theme filter will stop matching it. Do not send theme on update.

settings is schema-validated​

When the widget type was registered with a JSON Schema, settings is compiled and validated against it. A mismatch fails the whole request with:

{
"error": {
"status": 500,
"message": "Widget settings failed schema validation: [{\"instancePath\":\"/count\",\"message\":\"must be integer\"}]"
}
}

Widget types registered without a schema skip this step. Note that settings are stored as sent — nothing parses a JSON string into an array or object on your behalf, so send real arrays and objects.