Create Storefront Block
Append one draft widget to a storefront (Creator page).
type is required and must be one of the 14 widget types below. Omit layout
to use that type's default. layout of "" or null returns 422. Omit
config (or send {}) to use English defaults for that type and layout.
Sparse config is merged onto those defaults: nested objects merge, items
and fields replace when sent. Unknown keys at any nesting level return
422.
The new widget is appended at the next order (an empty page starts at 1).
The response includes a fresh preview_url of the saved draft. Visitors still
see the last published page at public_url until
Publish Storefront.
type cannot be changed later — hide the widget and create a new one. See
Update Storefront Block.
Request
POST /storefronts/{id}/blocks
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Storefront UUID |
Request Body
Unknown top-level keys are rejected. Do not send id or order — both are
assigned in the response.
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Widget type. One of the 14 types. |
layout | string | No | Layout token for this type. Omit for the type default. Must be in that type's allowlist. "" and null return 422. |
is_hidden | boolean | No | Hide the widget from visitors. Default false. |
is_hidden_on_mobile | boolean | No | Hide the widget on mobile viewports. Default false. |
config | object | No | Sparse object merged onto defaults for this type and layout. See Config keys. |
Defaults are for the type and layout together. A product created with
layout 6x3 is not a 3x3 product with the token swapped.
product_id (and collection items[].product_id) must be a product UUID of
this store, or "". Integers and UUIDs from another store return 422.
list_uuid must be a UUID or "". Empty identifiers and clearable URL
strings store as "", not null.
Widget Types
type must be one of:
product, product_collection, link, media, gallery, map, zencal,
logotypes, contact_form, newsletter_signup, free_download, text,
space, divider.
Layouts
Omit layout to use the default. A token that is not in that type's list,
an empty string, or null returns 422.
| Type | Default | Allowed layouts |
|---|---|---|
product | 3x3 | 6x4, 6x3, 3x4, 3x3, 2x3, 3x2, 6x1 |
product_collection | slider | slider, list, grid |
link | 6x1 | 6x1, 6x2, 3x1, 2x1, 2x2, 1x1 |
media | 3x3 | 6x4, 6x3, 6x2, 6x1, 4x4, 4x2, 4x1, 3x3, 3x2, 3x1, 2x3, 2x2 |
gallery | grid | grid, slider, asymmetric, asymmetric-mosaic |
map | 3x3 | 6x2, 4x2, 3x3, 3x2, 2x3, 2x2 |
zencal | 6x4 | 6x4 |
logotypes | 3x1 | 1x1, 2x1, 3x1 |
contact_form | 6x3 | 6x3, 6x4, 3x3, 3x4 |
newsletter_signup | 6x3 | 6x3, 6x2, 3x3 |
free_download | 6x2 | 6x3, 6x2, 2x2, 3x1 |
text | 6x1 | same grid presets as media |
space | space-md | space-xs, space-sm, space-md, space-lg, space-xl |
divider | divider-md | divider-xs, divider-sm, divider-md, divider-lg, divider-xl |
Config Keys
Every type accepts chrome keys: use_block_padding, use_block_surface, and
is_block_highlighted (booleans). Unknown config keys return 422.
Nested objects (photo, media, hideable {value, is_hidden}, privacy,
file) sparse-merge. items and fields replace the whole array when
sent.
| Type | Config keys (plus chrome) | Notes |
|---|---|---|
product | product_id, photo, title, price, old_price, description, link, cta_label, ownership_status, owned_cta_label | ownership_status: show_all, hide_owned, or mark_as_owned. product_id UUID or "". link absolute HTTP(S) or "". cta_label is hideable; owned_cta_label is a string. photo is a Photo. |
product_collection | title, columns, product_layout, show_prices, show_images, show_description, show_buttons, cta_label, source, sort_order, ownership_status, owned_cta_label, dynamic_rule, enable_show_more, initial_visible_count, items | title is hideable; cta_label is a string. source: manual or dynamic. sort_order: manual, best_sellers, newest_first, or personalized. dynamic_rule: on_promotion, has_countdown_timer, owned_by_visitor, not_yet_owned, related_to_owned, or all_products. product_layout: vertical or horizontal. items replaces; each item is sparse-merged onto product-item defaults (product_id, photo, title, price, old_price, description, link, cta_label). items[].product_id is a product UUID of this store or "". |
text | content, vertical_position, horizontal_alignment | vertical_position: top, center, or bottom. horizontal_alignment: left, center, right, or justify. |
newsletter_signup | list_uuid, success_message, error_message, title, subtitle, cta_label, privacy, vertical_position | list_uuid UUID or "". title, subtitle, and privacy are hideable. cta_label is a string. |
contact_form | fields, privacy, cta_label, cta_action, webhook_url, email, error_message, success_message, vertical_position | cta_action: email or webhook_url. webhook_url absolute HTTP(S) or "". email a valid address or null. fields replaces. See Contact field and Privacy. |
map | embed_url | Absolute HTTP(S) or "". Omit config to use the default Google Maps embed. |
link | photo, title, description, link, cta_label | link absolute HTTP(S) or "". description and cta_label are hideable. |
media | media | Nested Photo. Default video_src_url is "". |
gallery | title, columns, gallery_layout, enable_show_more, initial_visible_count, items | title is hideable. gallery_layout: vertical, square, or horizontal. items replaces; each item is a Photo. Stored items are not padded to a minimum count. |
space | chrome only | Height is the layout token. |
divider | vertical_position | Plus chrome. vertical_position: top, center, or bottom. |
zencal | embed_url | Absolute HTTP(S) or "". Default "". |
logotypes | media | Nested Photo. Default fit is contain. |
free_download | list_uuid, file, success_message, error_message, photo, title, description, cta_label, privacy | list_uuid UUID or "". file is a File. description and privacy are hideable. |
Every URL must be an absolute http:// or https:// URL, or "" to leave
empty. Relative paths return 422.
Photo
Used as photo, media, and each gallery items[] entry. Sparse-merged.
Unknown keys return 422.
| Parameter | Type | Description |
|---|---|---|
media_type | string | image, video, or iframe |
src_url | string | Image URL, or "" to clear |
alt | string | Alt text |
video_src_url | string | Video URL, or "" to clear |
video_autoplay | boolean | Whether the video autoplays |
video_controls | boolean | Whether playback controls are shown |
video_loop | boolean | Whether the video loops |
iframe_src_url | string | Iframe URL, or "" to clear |
is_hidden | boolean | Whether the media is hidden |
fit | string | cover or contain |
Hideable
Copy that can be shown or hidden: { "value": string, "is_hidden": boolean }.
Sparse-merged. Unknown keys return 422. Distinct from the widget's
is_hidden flag.
File
Used as config.file on free_download. Sparse-merged. Host a file with
Upload Media (prepare type file) and send
the returned name and file_url with external: false.
| Parameter | Type | Description |
|---|---|---|
name | string | Display name |
url | string | File URL, or "" to clear. Absolute http:// or https:// only. |
external | boolean | Whether the URL is an external link |
Contact Field
Each contact_form fields[] item. The array replaces when sent.
| Parameter | Type | Description |
|---|---|---|
type | string | email, text, phone, textarea, or switch |
label | string | Field label |
placeholder | string | Placeholder text |
input_name | string | Submitted field name |
required | boolean | Whether the field is required |
order | integer | Display order of the field |
Privacy
contact_form privacy is this object (sparse-merged). Newsletter and
free-download privacy is hideable instead.
| Parameter | Type | Description |
|---|---|---|
is_hidden | boolean | Whether the privacy line is hidden |
text | string | Privacy copy |
link_label | string | Link label |
link | string | Privacy-policy URL, or "" to clear |
validation_message | string | Shown when the visitor must accept privacy to continue |
Example Requests
Type only — layout becomes 3x3, config is English defaults,
product_id is "":
curl -X POST "https://cart.easy.tools/api/v1/storefronts/b10f5980-d653-4494-ad1c-6a9d68c96d19/blocks" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "product"
}'
Product with a layout and a store product UUID. Other config keys stay at
defaults for product + 6x3:
curl -X POST "https://cart.easy.tools/api/v1/storefronts/b10f5980-d653-4494-ad1c-6a9d68c96d19/blocks" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "product",
"layout": "6x3",
"config": {
"product_id": "3288e647-aab0-4289-957f-dac92e9b3ffe"
}
}'
Response
Success Response (201)
Returns the created widget plus a fresh preview_url of the saved draft. A
later GET of the same widget omits preview_url.
{
"id": "372a48fe-9035-4b1c-9257-1aa1fdff415d",
"type": "product",
"layout": "6x3",
"order": 3,
"is_hidden": false,
"is_hidden_on_mobile": false,
"config": {
"use_block_padding": true,
"use_block_surface": true,
"is_block_highlighted": false,
"product_id": "3288e647-aab0-4289-957f-dac92e9b3ffe",
"photo": {
"media_type": "image",
"src_url": "",
"alt": "",
"video_src_url": "",
"video_autoplay": true,
"video_controls": false,
"video_loop": true,
"iframe_src_url": "",
"is_hidden": false,
"fit": "cover"
},
"title": "Product title",
"price": { "value": "$0.00", "is_hidden": false },
"old_price": { "value": "", "is_hidden": false },
"description": { "value": "Product description", "is_hidden": false },
"link": "",
"cta_label": { "value": "Buy now", "is_hidden": false },
"ownership_status": "show_all",
"owned_cta_label": "Open"
},
"preview_url": "https://cart.easy.tools/storefront-preview/b10f5980-d653-4494-ad1c-6a9d68c96d19?expires=1788346311&signature=9c2ff69cbe27454f9a200f8d1b6ff1e8dc77701f411b01b7459d80d4344ac939"
}
preview_url is a fresh signature on every write. See
Draft preview.
Response Fields
Block fields are documented on the Block object. Creates
add preview_url:
| Field | Type | Nullable | Description |
|---|---|---|---|
preview_url | string | No | Temporary signed URL of the saved draft. Expires after about one hour. |
Error Responses
Bad Request (400)
Returned when {id} is not a UUID.
{
"message": "Invalid storefront ID"
}
Not Found Error (404)
Returned when the storefront UUID does not exist, belongs to another store, or points at a deleted storefront.
{
"message": "Storefront not found"
}
Validation Error (422 Unprocessable Entity)
Returned for an unknown key, a type that is not one of the 14, a layout
that is not allowed for that type (including "" and null), a product_id
that is not a product UUID of this store, an email that is not a valid
address, or a value outside an enum / URL rule.
{
"message": "The foo field is not allowed.",
"errors": {
"foo": [
"The foo field is not allowed."
]
}
}
Unauthorized (401)
{
"message": "Unauthenticated."
}
Forbidden (403)
Returned when your API key has not been granted access to the requested store.
{
"message": "You do not have API access to the requested store."
}
Too Many Requests (429)
Returned when you exceed the rate limit. The Retry-After response header gives
the number of seconds to wait.
{
"message": "Too many requests. Please retry after 60 seconds."
}