# Builderly API reference

Base URL: http://djotex.com
Generated from the running server on 2026-11-05T11:41:53.436Z. Every mounted route is listed with the access it needs, the parameters its handler reads and the data models it uses.

## Authentication

Send `Authorization: Bearer <token>` where a route needs a token. Add `?ionic=true` to get JSON from routes that also render pages.

- **Admin token** (`admin`): Bearer token of a site admin (Authorization: Bearer <token>).
- **User token** (`user`): Bearer token of a signed-in user of the site.
- **Optional user** (`optional-user`): Works without a token; a user token personalises the answer.
- **User with access** (`user-priv`): A signed-in user whose plan grants access to the item.
- **Page access** (`page-priv`): Pages limited to some users check the token when one is sent.
- **Webhook signature** (`webhook`): Called by a provider; verified by its signature.
- **reCAPTCHA** (`recaptcha`): Needs a reCAPTCHA token from the site form.
- **System** (`system`): Internal - called by the platform itself.

### For AI assistants: getting a token

Public endpoints (Access: Public) need no token. Everything else - changing the site, or reading its orders, bookings, customers and other private data - needs the site owner's API token.

You cannot create a token yourself, and you must not ask for the owner's password. Ask the user for a token instead - you can send them this message as it is:

> To work with your site through its API, I need an API token from you:
> 1. Sign in to your Builderly admin: https://admin.max-media.io
> 2. Open Developers > API reference (or go straight to https://admin.max-media.io/tabs/site-builder/api-docs).
> 3. Press "Generate token", copy the token and paste it here.
> The token acts as you, so only share it in this chat. You can cancel it at any time by signing out of all devices.

Then send it with every request as `Authorization: Bearer <token>`, and add `?ionic=true` to get JSON. Calls go to the site's own address (its domain), not to the reference's.

The token acts as the user: say what you will change and wait for their yes before any request that creates, changes or deletes something, never repeat the token back, and never put it in a link or a file you share.

## Status codes

| Code | Name | Meaning |
| --- | --- | --- |
| 200 | OK | It worked. The answer is the object or list asked for. |
| 201 | Created | The object was created. Some endpoints send it back; others send an empty body. |
| 202 | Accepted | The work was started and finishes in the background - for example a video being processed ({ "status": "processing" }). Check back on the object. |
| 204 | No Content | It worked, and there is nothing to send back - for example after removing a staff member. |
| 301 | Moved Permanently | The address changed; follow the Location header. |
| 400 | Bad Request | The request was refused as sent: a required field is missing, a value has the wrong type or fails validation. The body says what - often as plain text. |
| 401 | Unauthorized | No token, or one that is invalid or signed out. Without ?ionic=true, some endpoints answer this with the HTML login page instead of JSON. |
| 402 | Payment Required | The content needs a subscription: the user has no price plan that unlocks it. |
| 403 | Forbidden | The token is valid but may not do this - the record belongs to someone else, or the user is not an admin. AI builder endpoints also answer 403 when the account is out of credits. |
| 404 | Not Found | No object with that ID or URL slug, or no such endpoint. |
| 409 | Conflict | It clashes with the current state: the item is already being processed, a version is already applied, or an AI plan or preview is no longer available. |
| 410 | Gone | It existed but is no longer there - for example a comment already deleted on the social network. |
| 412 | Precondition Failed | A rule the request must meet is not met - for example push notifications of the same type at least 48 hours apart. |
| 418 | I'm a teapot | An account confirmation or password reset link was used with an email that does not match it. |
| 421 | Misdirected Request | The domain is not a Builderly site. Call the site's own address. |
| 422 | Unprocessable Content | The request was understood but could not be done - for example the AI could not read the website it was asked to clone. The message says what to try instead. |
| 429 | Too Many Requests | Slow down: the site's request limit was reached, an AI chat already has a request running, or the hourly limit of AI search answers is used up. Wait and retry. |
| 500 | Internal Server Error | Something failed on the server. Retrying later may work; if it keeps failing, the request may be one the endpoint does not handle. |
| 502 | Bad Gateway | A service the request depends on failed - for example the address a redirect forwards to. |
| 503 | Service Unavailable | Used by the lead form when an order is below its minimum value, not only for outages: read the message. |

Errors do not share one shape: depending on the endpoint the body is plain text, { "error": "..." }, { "message": "..." } or empty. Rely on the status code, and show the body to the user as the reason.

## Endpoints

### Pages

Pages (about pages) are the pages built in the editor or by the AI builder: a landing page, an "About us", a contact page, a legal page. A page is a list of containers - full-width sections - each holding columns, and each column holds components: text, headings, buttons, images, galleries, sliders, forms, and embeds of the site's products, services, blog posts and more. Pages also serve as reusable parts: the banner above the header, the content shown on a product page, the landing page behind a popup.

#### About Page

##### List all pages
`GET /about-pages`
Returns the site's pages, newest first unless a sort is given.
- Access: Public
- Returns: a list of AboutPage objects.
- Status codes: 200, 500
- Models: AboutPage

##### Retrieve a page
`GET /about-pages-by-id/:id`
Returns one page, found by its ID.
- Access: User with access, Optional user
- Path:
  - `id` (string): The ID of the page.
- Returns: the AboutPage object.
- Status codes: 200, 301, 401, 402, 404, 500
- Models: AboutPage

##### Update a page
`PATCH /about-pages/:id`
Update AboutPage by id.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Body:
  - `containers` (object[]): The page's content: sections, each with columns of components.
- Returns: the AboutPage object.
- Status codes: 200, 400, 401, 404, 500
- Models: AboutPage

##### Delete a page
`DELETE /about-pages/:id`
Delete AboutPage record by id.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Returns: an empty answer on success.
- Status codes: 200, 401, 404, 500
- Models: AboutPage, Product, Service, BlogPost, SiteMenu, Meta

##### Create a page
`POST /about-pages/:id/draft`
The editor's save: content goes to the draft, access settings to the live page.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Status codes: 200, 401, 500

##### Delete a page
`DELETE /about-pages/:id/draft`
Throw the draft away.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Returns: an empty answer on success.
- Status codes: 200, 401, 500

##### Create a page
`POST /about-pages/:id/preview-token`
A short-lived link query that shows one version on the public page.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Body:
  - `version_id` (string): The page's version id.
- Status codes: 200, 401, 404, 500

##### Publish the page
`POST /about-pages/:id/publish`
Make the draft the live page.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Status codes: 200, 401, 500

##### Revert the page
`POST /about-pages/:id/revert`
Publish a copy of an earlier version.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Body:
  - `version_id` (string): The page's version id.
- Status codes: 200, 401, 500

##### Create a page
`POST /about-pages/:id/rollout`
Show a version (the draft or an older one) to a share of the visitors, or. change the share. Ends a rollout of any other version of the page.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Body:
  - `version_id` (string): The page's version id.
  - `percent` (string): The page's percent.
- Status codes: 200, 401, 500

##### Delete a page
`DELETE /about-pages/:id/rollout`
Stop the rollout: everyone sees the live page again; the version stays.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Returns: an empty answer on success.
- Status codes: 200, 401, 500

##### Retrieve a page
`GET /about-pages/:id/versions`
The page's versions, newest first, without their content.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
- Status codes: 200, 401, 500

##### Update a page
`PATCH /about-pages/:id/versions/:versionId`
Name a version; named versions are never pruned.
- Access: Admin token
- Path:
  - `id` (string): The ID of the page.
  - `versionId` (string): The page's version id.
- Body:
  - `label` (string): The page's label.
- Status codes: 200, 401, 500

##### Retrieve a page
`GET /about-pages/:url`
Returns one page, found by its URL slug.
- Access: Page access, Optional user
- Path:
  - `url` (string): The page's URL slug, as in its address on the site.
- Returns: the AboutPage object.
- Status codes: 200, 301, 401, 402, 404, 500
- Models: AboutPage

#### About Page Import

##### List all section imports
`GET /about-section-imports`
Returns the site's section imports, newest first unless a sort is given.
- Access: Public
- Status codes: 200, 500

##### Create a section import
`POST /about-section-imports`
Creates a section import from the fields sent.
- Access: Admin token
- Body:
  - `body` (string): The section import's body.
- Status codes: 201, 400, 401, 500

##### Retrieve a section import
`GET /about-section-imports/:id`
Returns one section import, found by its ID.
- Access: Public
- Path:
  - `id` (string): The ID of the section import.
- Status codes: 200, 404, 500

#### Custom Page

##### List all custom pages
`GET /custom_pages`
Returns the site's custom pages, newest first unless a sort is given.
- Access: Public
- Returns: a list of CustomPage objects.
- Status codes: 200, 500
- Models: CustomPage

##### Retrieve a custom page
`GET /custom_pages/:id`
Returns one custom page, found by its ID.
- Access: Public
- Path:
  - `id` (string): The ID of the custom page.
- Returns: the CustomPage object.
- Status codes: 200, 404, 500
- Models: CustomPage

##### Update a custom page
`PATCH /custom_pages/:id`
Update CustomPage by id.
- Access: Admin token
- Path:
  - `id` (string): The ID of the custom page.
- Returns: the CustomPage object.
- Status codes: 200, 400, 401, 404, 500
- Models: CustomPage

##### Delete a custom page
`DELETE /custom_pages/:id`
Delete CustomPage record by id.
- Access: Admin token
- Path:
  - `id` (string): The ID of the custom page.
- Returns: an empty answer on success.
- Status codes: 200, 401, 404, 500
- Models: CustomPage

#### Publish

##### Retrieve the publish
`GET /publish`
Get publish settings.
- Access: Public
- Returns: the Publish object.
- Status codes: 200, 500
- Models: Publish

##### Create a publish
`POST /publish`
Set new publish settings.
- Access: Admin token
- Returns: the Publish object.
- Status codes: 201, 400, 401, 500
- Models: Publish

##### Retrieve a publish (deprecated)
`GET /publish/:id`
Returns one publish, found by its ID.
- Access: Public
- Path:
  - `id` (string): The ID of the publish.
- Returns: the Publish object.
- Status codes: 200, 404, 500
- Models: Publish

##### Update a publish
`PATCH /publish/:id`
Update publish settings by id.
- Access: Admin token
- Path:
  - `id` (string): The ID of the publish.
- Returns: the Publish object.
- Status codes: 200, 400, 401, 404, 500
- Models: Publish

#### AI builder

##### Add an add section
`POST /add-section`
Use it to add, insert, append, or place a new section on an existing page.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the add section, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
  - `item_data` (string): The current add section, as JSON - the AI changes it and returns the result.
  - `current_page_url` (string): The add section's current page url.
  - `section_blueprint` (string): The add section's section blueprint.
  - `section_style_blueprint` (string): The add section's section style blueprint.
  - `explicit_target` (string): The add section's explicit target.
  - `parsed_intent` (string): The add section's parsed intent.
- Status codes: 200, 401, 500
- Models: ChatMessage

##### Create a commit page
`POST /commit-about-page-preview`
Creates a commit page from the fields sent.
- Access: User token
- Body:
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `messageId` (string): The commit page's message id.
- Status codes: 200, 401, 404, 500
- Models: ChatMessage

##### Confirm an AI action
`POST /confirm-ai-action`
Confirm an AI action.
- Access: User token
- Body:
  - `messageId` (string): The AI action's message id.
  - `confirmed` (string): The AI action's confirmed.
  - `skip_steps` (string): The AI action's skip steps.
  - `choice` (string): The AI action's choice.
  - `item_data` (string): The current AI action, as JSON - the AI changes it and returns the result.
- Status codes: 200, 400, 401, 403, 409, 500
- Models: ChatMessage

##### Confirm a page plan
`POST /confirm-page-plan`
Confirm a page plan.
- Access: User token
- Body:
  - `messageId` (string): The page plan's message id.
- Status codes: 200, 400, 401, 403, 500
- Models: ChatMessage

##### Create a page with AI
`POST /create-new-page`
Create a page from a prompt, written by the AI builder.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the page, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
  - `socketId` (string): The page's socket id.
- Status codes: 200, 401, 500

##### Generate a page with AI
`POST /generate-page`
Use it for a new standalone page, from a prompt or modelled on one the site has.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the page, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
  - `socketId` (string): The page's socket id.
- Status codes: 200, 401, 500

##### Modify a footer with AI
`POST /modify-footer`
Use it to change footer components and columns.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the footer, in plain words - any language.
  - `item_data` (string): The current footer, as JSON - the AI changes it and returns the result.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
- Status codes: 200, 401, 500

##### Modify a header with AI
`POST /modify-header`
Use it to change link font size and weight, links positioning and justification, action button text color and background, banner style of the header of the site.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the header, in plain words - any language.
  - `item_data` (string): The current header, as JSON - the AI changes it and returns the result.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
- Status codes: 200, 401, 500

##### Modify a page with AI
`POST /modify-page`
Use it to edit or update existing page content or structure, including page colors.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the page, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `item_id` (string): The ID of the page to change.
  - `item_ids` (string): The page's item ids.
- Status codes: 200, 401

##### Modify a page permission with AI
`POST /modify-page-permissions`
Use it to require login to access content sections, or set up a paywall when social mode is off. Makes products, services, blogposts, videos, programs, or custom pages require login or subscription.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the page permission, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
  - `item_id` (string): The ID of the page permission to change.
- Status codes: 200, 401, 500
- Models: ChatMessage

##### Modify a theme with AI
`POST /modify-theme`
Use it to change the look, colors, fonts, or layout theme of the site, including the header background and link colors.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the theme, in plain words - any language.
  - `item_data` (string): The current theme, as JSON - the AI changes it and returns the result.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
- Status codes: 200, 401, 500

##### Create an onboarding intent planner
`POST /onboarding-intent-planner`
Used only for the very first user prompt (or first session) when you describe a business/intent. The first message, normalized into something the planner can build from. WHAT THIS IS FOR. 67% of real first prompts name a business and stop — `Автокъща`, `майстор плочкаджия`, `детски учител`, with no verb and no object (see USER_LOGS_ANALYSIS.md §2). This rewrites that into one implementation-ready sentence and, usefully, encodes which platform area it needs in plain language, so `apply_domain_recipe` downstream picks store vs bookings vs informational. IT USED TO ASK FOR A PAGE. The rewrite began "Създай страница за …" and the redirect went to `create-new-page`, which layout-plans ONE page and parks at "Awaiting confirmation". So the single most common thing anyone types produced a page and a template site: no services, no products, no collections, no legal pages, no locale pass — all of which the recipes build. Routing was never the problem; the router sent 16 of 20 real first prompts to a whole-site destination, and 12–13 of those were THIS route, which then threw the site away. Now it asks for a site and redirects to `create-plan`. That is safe because `/create-plan` orchestrates with `planOnly: true` (planners.js) — it gets propose_plan, apply_domain_recipe and ask_clarification but no routes, so it cannot route back here or sideways into a single item. The redirect carries the rewrite in `response`, which is what the client turns into the next request's `prompt` — this is a SECOND-hop redirect, handled at ai-components-chat.component.ts:2437 (`handleRes(res2.target_url, res2.response, …)`). First-hop redirects out of the orchestrator use `user_prompt` instead; do not confuse the two.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the onboarding intent planner, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
- Status codes: 200, 401, 500
- Models: ChatMessage

##### Create a regenerate component
`POST /regenerate-component`
Creates a regenerate component from the fields sent.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the regenerate component, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
  - `item_data` (string): The current regenerate component, as JSON - the AI changes it and returns the result.
  - `current_page_url` (string): The regenerate component's current page url.
  - `explicit_target` (string): The regenerate component's explicit target.
  - `save_user_message` (string): The regenerate component's save user message.
  - `preview_only` (string): The regenerate component's preview only.
- Status codes: 200, 401, 500

##### Create a regenerate section
`POST /regenerate-section`
From the editor: regenerates the currently selected section of the open about-page.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the regenerate section, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
  - `item_data` (string): The current regenerate section, as JSON - the AI changes it and returns the result.
  - `current_page_url` (string): The regenerate section's current page url.
  - `explicit_target` (string): The regenerate section's explicit target.
  - `save_user_message` (string): The regenerate section's save user message.
  - `section_blueprint` (string): The regenerate section's section blueprint.
  - `section_style_blueprint` (string): The regenerate section's section style blueprint.
  - `preview_only` (string): The regenerate section's preview only.
  - `regeneration_mode` (string): The regenerate section's regeneration mode.
  - `replacement_strategy` (string): The regenerate section's replacement strategy.
  - `source_small_target` (string): The regenerate section's source small target.
- Status codes: 200, 401, 500
- Models: ChatMessage

##### Create a reopen page
`POST /reopen-about-page-preview`
Creates a reopen page from the fields sent.
- Access: User token
- Body:
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `messageId` (string): The reopen page's message id.
- Status codes: 200, 401, 404, 409, 500
- Models: ChatMessage

##### Create a section option
`POST /section-options`
Creates a section option from the fields sent.
- Access: User token
- Status codes: 200, 401, 410

##### Create a section style option
`POST /section-style-options`
Creates a section style option from the fields sent.
- Access: User token
- Status codes: 200, 401, 410

##### Update a footer with AI
`POST /update-footer`
Update a footer from a prompt, written by the AI builder.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the footer, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
- Status codes: 200, 401, 500

##### Update a header with AI
`POST /update-header`
Update a header from a prompt, written by the AI builder.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the header, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
- Status codes: 200, 401, 500

##### Update a page with AI
`POST /update-page`
From the editor: applies a targeted edit to the about-page currently open in the editor.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the page, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
  - `item_data` (string): The current page, as JSON - the AI changes it and returns the result.
  - `current_page_url` (string): The page's current page url.
  - `explicit_target` (string): The page's explicit target.
  - `item_id` (string): The ID of the page to change.
  - `pending_selection_intent` (string): The page's pending selection intent.
  - `save_user_message` (string): The page's save user message.
  - `preview_only` (string): The page's preview only.
  - `regeneration_mode` (string): The page's regeneration mode.
  - `force_targeted_modifier` (string): The page's force targeted modifier.
  - `redesign_another` (string): The page's redesign another.
- Status codes: 200, 401, 500
- Models: ChatMessage

##### Update a theme with AI
`POST /update-theme`
Update a theme from a prompt, written by the AI builder.
- Access: User token
- Body:
  - `prompt` (string): What the AI should do with the theme, in plain words - any language.
  - `chatId` (string): The AI chat this request belongs to; its history and language carry over. Leave it out to start a new chat.
  - `aiVersion` (string): Which version of the AI builder to use; leave it out for the current one.
- Status codes: 200, 401, 500

## Page components

A page (AboutPage) has containers; each container has columns; each column has components, each with a `type` and a `size` from 1 to 12 on the column's grid.

- `empty` (Text): Empty space, for spacing.
- `hr` (Text): A horizontal line across the component's width.
- `heading` (Text): A heading.
- `text` (Text): Rich text, stored as HTML in innerHTML.
- `collapsible_container` (Text): An accordion: questions or titles that open to show their answer - FAQs, details. Its look is in collapsible_container_settings. Settings in `collapsible_container`.
- `tabs_container` (Text): Tabs: the same items as an accordion, shown one at a time behind tab buttons. Settings in `tabs`.
- `text_auto_slider` (Text): A strip of phrases scrolling across endlessly - a marquee. Settings in `text_auto_slider`.
- `button` (Button): A button linking to an address (href) or a page of the site (router_link); its label is innerHTML, its look buttonOptions.
- `icon-0 … icon-9` (Icon): An icon with optional heading, sub-heading and text, in ten layouts (centred, left, right).
- `img` (Media): One image or video, optionally linking somewhere.
- `gallery` (Media): A grid of images that open larger when clicked.
- `big-gallery` (Media): One large image with thumbnails below to switch it.
- `carousel` (Media): Images shown one at a time, with arrows and dots.
- `carousel-multiple` (Media): Images shown several at a time, sliding.
- `hoverable` (Media): An image that changes to another when the pointer is over it. Settings in `hoverable_media`.
- `image_dragger` (Media): Two images on top of each other with a handle to drag between them - before and after. Settings in `image_dragger`.
- `image_auto_slider` (Media): A strip of images scrolling across endlessly. Settings in `image_auto_slider`.
- `prod` (Product): One of the site's products, with its price and a button to buy. Settings in `products`.
- `prod_gallery` (Product): A grid or slider of the site's products - the ones listed in products. Settings in `products`.
- `serv` (Service): One of the site's services, with a button to book it. Settings in `services`.
- `serv_gallery` (Service): A grid or slider of the site's services - the ones listed in services. Settings in `services`.
- `blog` (Blog post): One blog post, as a card linking to it. Settings in `blogposts`.
- `blog_gallery` (Blog post): A grid or slider of the site's blog posts - the ones listed in blogposts. Settings in `blogposts`.
- `video` (Video): One of the site's videos. Settings in `videos`.
- `videos_gallery` (Video): A gallery of the site's videos. Settings in `videos`.
- `form` (Contact & form): A contact form; what visitors send arrives as a lead. Its fields are in contactForm. Settings in `contactForm`.
- `newsletter` (Contact & form): A newsletter sign-up: name, email and a button. Settings in `contactForm`.
- `map` (Other): A map, from embed code (usually Google Maps). Settings in `map`.
- `quiz` (Other): A quiz: questions with answers, an explanation for each and a result by score. Settings in `quiz`.
- `swiper` (Other): A slider whose slides are other pages' sections. Settings in `swiper`.
- `facebook` (Other): An embedded Facebook page or post. Settings in `facebook`.
- `custom` (Other): A custom block with its own HTML (innerHTML), CSS (innerCSS) and JavaScript (innerJS).
- `countdown` (Other): A countdown to a date, in boxes of days, hours, minutes and seconds; then a text of your choice. Settings in `countdown`.
- `smart_search` (Other): Search over the site's content, with filters by kind, price and date and an optional AI answer. Settings in `smart_search`.
- `serv_multi_booking` (Service): The booking flow on a page: choose a service, a day, a time and a staff member, then confirm.
- `price_plans` (Other): The site's price plans, side by side to compare and subscribe. Settings in `price_plans`.
- `scroll_sequence` (Media): An image sequence that plays as the visitor scrolls. Settings in `scroll_sequence`.

## Objects

### AboutPage

A page built in the editor or by the AI builder. Its content is in containers: each container is a full-width section with columns, and each column has components, laid out on a 12-column grid by their size.

- `requiresLogin`: Boolean, default false - Only signed-in users can see the page.
- `requiresSubscription`: Boolean, default false - Only subscribers of one of price_plan_ids can see the page.
- `price_plan_ids`: String[] - The price plans whose subscribers can see the page, with requiresSubscription.
- `title`: String, required - The page's title, shown in the browser tab and in the admin.
- `url`: String, required - The page's address on the site: /<url>. Unique.
- `redirectUrl`: String - When set, visitors who open the page are sent to this address instead.
- `legal_kind`: String - For legal pages: which document it is, in English (Privacy Policy, Terms of Service...).
- `isPartial`: Boolean, default false - A part of other pages (a banner, a block inside a product page), not a page of its own.
- `showTo`: String, default "0" - Who sees the page: "0" everyone, "1" signed-in users only, "2" visitors who are not signed in.
- `showHeader`: Boolean, default true - Show the site's header on the page.
- `showFooter`: Boolean, default true - Show the site's footer on the page.
- `ai_context_v2`: Mixed
- `ai_surfaces_v2`: Mixed
- `ai_overview`: String - A short summary of the page the AI builder keeps for its own use.
- `ai_generation_metadata`: Mixed
- `containers`: Object[] - The page's content: sections, each with columns of components.
  - `isFluid`: Boolean, default false
  - `parallaxEnabled`: Boolean
  - `parallaxAmount`: Number
  - `parallaxZoom`: Number
  - `continuousEnabled`: Boolean, default false
  - `continuousEffect`: String, default "none"
  - `continuousDuration`: Number, default 2400
  - `continuousIntensity`: Number, default 4
  - `backgroundImage`: String
  - `backgroundSize`: String
  - `backgroundPosition`: String
  - `backgroundRepeat`: String
  - `backgroundVideo`: String
  - `backgroundColor`: String
  - `backgroundGradient.enabled`: Boolean, default false
  - `backgroundGradient.type`: String
  - `backgroundGradient.degrees`: Number
  - `backgroundGradient.radialShape`: String
  - `backgroundGradient.radialPosition`: String
  - `backgroundGradient.colors`: Object[]
    - `color`: String
    - `percentage`: Number
    - `_id`: ObjectId
  - `backgroundGradient.css`: String
  - `backgroundParallax`: Boolean, default false
  - `minHeight`: Number
  - `minHeightMobile`: Number
  - `padding_top.value`: String, default ""
  - `padding_top.unit`: String, default "px"
  - `padding_top.mobile_value`: String, default ""
  - `padding_top.mobile_unit`: String, default "px"
  - `padding_right.value`: String, default ""
  - `padding_right.unit`: String, default "px"
  - `padding_right.mobile_value`: String, default ""
  - `padding_right.mobile_unit`: String, default "px"
  - `padding_bottom.value`: String, default ""
  - `padding_bottom.unit`: String, default "px"
  - `padding_bottom.mobile_value`: String, default ""
  - `padding_bottom.mobile_unit`: String, default "px"
  - `padding_left.value`: String, default ""
  - `padding_left.unit`: String, default "px"
  - `padding_left.mobile_value`: String, default ""
  - `padding_left.mobile_unit`: String, default "px"
  - `allowOffset`: Boolean, default false
  - `offsetX`: Number, default 0
  - `offsetY`: Number, default 0
  - `offsetMobileX`: Number, default 0
  - `offsetMobileY`: Number, default 0
  - `zIndex`: Number, default 0
  - `wrap_reverse`: Boolean, default false
  - `borderRadius`: Number, default 0
  - `marginX`: Number, default 0
  - `editorName`: String
  - `horizontalScroll`: Boolean, default false
  - `horizontalScrollWidth`: Number, default 100
  - `overflowX`: String, default "hidden"
  - `columns`: Object[]
    - `size`: String
    - `hoverGroup`: Boolean
    - `parallaxEnabled`: Boolean
    - `parallaxAmount`: Number
    - `parallaxZoom`: Number
    - `continuousEnabled`: Boolean, default false
    - `continuousEffect`: String, default "none"
    - `continuousDuration`: Number, default 2400
    - `continuousIntensity`: Number, default 4
    - `sticky`: Boolean, default false
    - `justify`: String
    - `align`: String
    - `onHoverActions`: Boolean, default false
    - `onHoverTransition`: Number, default 0.3
    - `backgroundColor`: String
    - `backgroundGradient.enabled`: Boolean, default false
    - `backgroundGradient.type`: String
    - `backgroundGradient.degrees`: Number
    - `backgroundGradient.radialShape`: String
    - `backgroundGradient.radialPosition`: String
    - `backgroundGradient.colors`: Object[]
      - `color`: String
      - `percentage`: Number
      - `_id`: ObjectId
    - `backgroundGradient.css`: String
    - `backgroundColorOnHover`: String
    - `innerBackgroundColor`: String
    - `innerBackgroundGradient.enabled`: Boolean, default false
    - `innerBackgroundGradient.type`: String
    - `innerBackgroundGradient.degrees`: Number
    - `innerBackgroundGradient.radialShape`: String
    - `innerBackgroundGradient.radialPosition`: String
    - `innerBackgroundGradient.colors`: Object[]
      - `color`: String
      - `percentage`: Number
      - `_id`: ObjectId
    - `innerBackgroundGradient.css`: String
    - `innerBackgroundColorOnHover`: String
    - `backgroundImage`: String
    - `backgroundSize`: String
    - `backgroundPosition`: String
    - `backgroundRepeat`: String
    - `backgroundVideo`: String
    - `paddingX`: Number, default 0
    - `paddingXOnHover`: Number, default 0
    - `paddingY`: Number, default 0
    - `paddingYOnHover`: Number, default 0
    - `innerPaddingX`: Number, default 0
    - `innerPaddingXOnHover`: Number, default 0
    - `innerPaddingY`: Number, default 0
    - `allowOffset`: Boolean, default false
    - `offsetX`: Number, default 0
    - `offsetY`: Number, default 0
    - `offsetMobileX`: Number, default 0
    - `offsetMobileY`: Number, default 0
    - `zIndex`: Number, default 0
    - `innerPaddingYOnHover`: Number, default 0
    - `paddingOptions`: String
    - `paddingOptionsOnHover`: String
    - `innerPaddingOptions`: String
    - `borderOptions`: String
    - `borderOptionsOnHover`: String
    - `card`: Boolean, default false
    - `borderRadius`: Number, default 0
    - `borderRadiusOnHover`: Number, default 0
    - `borderType`: String, default "none"
    - `borderTypeOnHover`: String, default "none"
    - `borderWidth`: Number, default 0
    - `borderWidthOnHover`: Number, default 0
    - `borderColor`: String, default ""
    - `borderColorOnHover`: String, default ""
    - `components`: Object[]
      - `type`: String
      - `size`: String, default "12"
      - `sticky`: Boolean, default false
      - `paddingX`: Number, default 0
      - `paddingY`: Number, default 0
      - `allowOffset`: Boolean, default false
      - `offsetX`: Number, default 0
      - `offsetY`: Number, default 0
      - `offsetMobileX`: Number, default 0
      - `offsetMobileY`: Number, default 0
      - `zIndex`: Number, default 0
      - `position`: String, one of static | relative | absolute | fixed | sticky, default "static"
      - `top.value`: String, default "0"
      - `top.unit`: String, default "px"
      - `top.mobile_value`: String, default ""
      - `top.mobile_unit`: String, default "px"
      - `left.value`: String, default "0"
      - `left.unit`: String, default "px"
      - `left.mobile_value`: String, default ""
      - `left.mobile_unit`: String, default "px"
      - `right.value`: String, default "0"
      - `right.unit`: String, default "px"
      - `right.mobile_value`: String, default ""
      - `right.mobile_unit`: String, default "px"
      - `bottom.value`: String, default "0"
      - `bottom.unit`: String, default "px"
      - `bottom.mobile_value`: String, default ""
      - `bottom.mobile_unit`: String, default "px"
      - `custom_measurements`: Boolean, default false
      - `edit_type`: String, default "simple"
      - `custom_width.value`: Mixed
      - `custom_width.unit`: String, default "px"
      - `custom_width.mobile_value`: Mixed
      - `custom_width.mobile_unit`: String, default "px"
      - `custom_min_width.value`: Mixed
      - `custom_min_width.unit`: String, default "px"
      - `custom_min_width.mobile_value`: Mixed
      - `custom_min_width.mobile_unit`: String, default "px"
      - `custom_max_width.value`: Mixed
      - `custom_max_width.unit`: String, default "px"
      - `custom_max_width.mobile_value`: Mixed
      - `custom_max_width.mobile_unit`: String, default "px"
      - `custom_height.value`: Mixed
      - `custom_height.unit`: String, default "px"
      - `custom_height.mobile_value`: Mixed
      - `custom_height.mobile_unit`: String, default "px"
      - `custom_min_height.value`: Mixed
      - `custom_min_height.unit`: String, default "px"
      - `custom_min_height.mobile_value`: Mixed
      - `custom_min_height.mobile_unit`: String, default "px"
      - `custom_max_height.value`: Mixed
      - `custom_max_height.unit`: String, default "px"
      - `custom_max_height.mobile_value`: Mixed
      - `custom_max_height.mobile_unit`: String, default "px"
      - `object_fit`: String, default "none"
      - `aspect_ratio.custom`: Boolean, default false
      - `aspect_ratio.ratio`: String, default ""
      - `min_height`: Number, default 0
      - `margin_left.value`: Mixed
      - `margin_left.unit`: String, default "px"
      - `margin_left.mobile_value`: Mixed
      - `margin_left.mobile_unit`: String, default "px"
      - `margin_right.value`: Mixed
      - `margin_right.unit`: String, default "px"
      - `margin_right.mobile_value`: Mixed
      - `margin_right.mobile_unit`: String, default "px"
      - `margin_top.value`: Mixed
      - `margin_top.unit`: String, default "px"
      - `margin_top.mobile_value`: Mixed
      - `margin_top.mobile_unit`: String, default "px"
      - `margin_bottom.value`: Mixed
      - `margin_bottom.unit`: String, default "px"
      - `margin_bottom.mobile_value`: Mixed
      - `margin_bottom.mobile_unit`: String, default "px"
      - `padding_left.value`: Mixed
      - `padding_left.unit`: String, default "px"
      - `padding_left.mobile_value`: Mixed
      - `padding_left.mobile_unit`: String, default "px"
      - `padding_right.value`: Mixed
      - `padding_right.unit`: String, default "px"
      - `padding_right.mobile_value`: Mixed
      - `padding_right.mobile_unit`: String, default "px"
      - `padding_top.value`: Mixed
      - `padding_top.unit`: String, default "px"
      - `padding_top.mobile_value`: Mixed
      - `padding_top.mobile_unit`: String, default "px"
      - `padding_bottom.value`: Mixed
      - `padding_bottom.unit`: String, default "px"
      - `padding_bottom.mobile_value`: Mixed
      - `padding_bottom.mobile_unit`: String, default "px"
      - `border_left.color`: String
      - `border_left.width.value`: Mixed
      - `border_left.width.unit`: String, default "px"
      - `border_left.style`: String
      - `border_right.color`: String
      - `border_right.width.value`: Mixed
      - `border_right.width.unit`: String, default "px"
      - `border_right.style`: String
      - `border_top.color`: String
      - `border_top.width.value`: Mixed
      - `border_top.width.unit`: String, default "px"
      - `border_top.style`: String
      - `border_bottom.color`: String
      - `border_bottom.width.value`: Mixed
      - `border_bottom.width.unit`: String, default "px"
      - `border_bottom.style`: String
      - `border_top_left_radius.value`: Mixed
      - `border_top_left_radius.unit`: String, default "px"
      - `border_top_right_radius.value`: Mixed
      - `border_top_right_radius.unit`: String, default "px"
      - `border_bottom_left_radius.value`: Mixed
      - `border_bottom_left_radius.unit`: String, default "px"
      - `border_bottom_right_radius.value`: Mixed
      - `border_bottom_right_radius.unit`: String, default "px"
      - `custom_border_radius.value`: Number
      - `custom_border_radius.unit`: String, default "px"
      - `specific_border_radius`: Boolean, default false
      - `box_shadows`: Object[]
      - `mix_blend_mode`: String, one of normal | multiply | screen | overlay | darken | lighten | color-dodge | color-burn | hard-light | soft-light | difference | exclusion | hue | saturation | color | luminosity, default "normal"
      - `paddingOptions`: String
      - `backgroundColor`: String
      - `backgroundGradient.enabled`: Boolean, default false
      - `backgroundGradient.type`: String
      - `backgroundGradient.degrees`: Number
      - `backgroundGradient.radialShape`: String
      - `backgroundGradient.radialPosition`: String
      - `backgroundGradient.colors`: Object[]
      - `backgroundGradient.css`: String
      - `card`: Boolean, default false
      - `borderRadius`: Number, default 0
      - `borderType`: String, default "none"
      - `borderWidth`: Number, default 0
      - `borderColor`: String, default ""
      - `borderOptions`: String
      - `showOnMobile`: Boolean, default true
      - `showOnDesktop`: Boolean, default true
      - `icon`: String
      - `iconSettings.size`: Number, default 40
      - `iconSettings.color`: String, default "var(--ion-color-primary)"
      - `heading`: String
      - `subHeading`: String
      - `heading_alignment`: String
      - `innerHTML`: String
      - `innerCSS`: String
      - `innerJS`: String
      - `lineHeight`: String, default "normal"
      - `medias`: Object[]
      - `products`: ObjectId[], → Product
      - `product_gallery.design_layout`: String, default ""
      - `product_gallery.mode`: String, default "card"
      - `product_gallery.columns`: Number, default 3
      - `product_gallery.placeholderItems`: Object[]
      - `product_gallery.placeholderPlateColor`: String, default ""
      - `product_gallery.showName`: Boolean, default true
      - `product_gallery.showPrice`: Boolean, default true
      - `product_gallery.showDescription`: Boolean, default false
      - `product_gallery.showBrand`: Boolean, default false
      - `product_gallery.showRibbon`: Boolean, default false
      - `product_gallery.showRating`: Boolean, default false
      - `product_gallery.showCart`: Boolean, default false
      - `product_gallery.showLink`: Boolean, default false
      - `product_gallery.linkLabel`: String, default "View product"
      - `product_gallery.imageAspectRatio`: String, default "1/1"
      - `product_gallery.imageHeight`: Number, default 260
      - `product_gallery.imageRadius`: Number, default 20
      - `product_gallery.imageObjectFit`: String, default "cover"
      - `product_gallery.cardBackgroundColor`: String, default "var(--ion-card-background, #ffffff)"
      - `product_gallery.cardBorderRadius`: Number, default 28
      - `product_gallery.cardBorderWidth`: Number, default 0
      - `product_gallery.cardBorderColor`: String, default "transparent"
      - `product_gallery.cardPaddingX`: Number, default 20
      - `product_gallery.cardPaddingY`: Number, default 20
      - `product_gallery.gap`: Number, default 24
      - `product_gallery.boxShadowColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.22)"
      - `product_gallery.boxShadowOffsetY`: Number, default 20
      - `product_gallery.boxShadowBlurRadius`: Number, default 40
      - `product_gallery.boxShadowSpreadRadius`: Number, default -22
      - `product_gallery.fieldBackground`: String, default "transparent"
      - `product_gallery.hairlineColor`: String, default "transparent"
      - `product_gallery.hairlineWidth`: Number, default 0
      - `product_gallery.maxWidth`: Number, default 0
      - `product_gallery.scrimGradient`: String, default ""
      - `product_gallery.align`: String, default "left"
      - `product_gallery.accentColor`: String, default "var(--ion-color-primary, #3880ff)"
      - `product_gallery.nameColor`: String, default "var(--ion-text-color, #1e293b)"
      - `product_gallery.nameFontSize`: String, default "1em"
      - `product_gallery.nameFontWeight`: String, default "700"
      - `product_gallery.nameFontFamily`: String, default ""
      - `product_gallery.nameTransform`: String, default "none"
      - `product_gallery.priceColor`: String, default "var(--ion-color-primary, #3880ff)"
      - `product_gallery.priceFontSize`: String, default "0.94em"
      - `product_gallery.priceFontWeight`: String, default "600"
      - `product_gallery.priceFontFamily`: String, default ""
      - `product_gallery.priceFontStyle`: String, default "normal"
      - `product_gallery.metaColor`: String, default "rgba(var(--ion-text-color-rgb, 30,41,59), 0.66)"
      - `product_gallery.titlePosition`: String, default "bottom"
      - `product_gallery.imageSide`: String, default "left"
      - `product_gallery.imagePadding`: Number, default 0
      - `product_gallery.boxShadowOffsetX`: Number, default 0
      - `product_gallery.descFontSize`: String, default "13px"
      - `product_gallery.descFontWeight`: String, default "400"
      - `product_gallery.animation`: String, default "none"
      - `product_gallery.animationDuration`: Number, default 0
      - `product_gallery.animationDelay`: Number, default 0
      - `product_gallery.showCustomFields`: Boolean, default false
      - `product_gallery.cardDesign`: String, default ""
      - `product_gallery.carouselLoop`: Boolean, default false
      - `product_gallery.carouselControls`: Boolean, default false
      - `product_gallery.customCss`: String, default ""
      - `services`: ObjectId[], → Service
      - `service_gallery.design_layout`: String, default ""
      - `service_gallery.mode`: String, default "card"
      - `service_gallery.columns`: Number, default 3
      - `service_gallery.placeholderItems`: Object[]
      - `service_gallery.placeholderPlateColor`: String, default ""
      - `service_gallery.showTitle`: Boolean, default true
      - `service_gallery.showPrice`: Boolean, default true
      - `service_gallery.showDescription`: Boolean, default false
      - `service_gallery.showDuration`: Boolean, default false
      - `service_gallery.durationLabel`: String, default "min"
      - `service_gallery.showImage`: Boolean, default true
      - `service_gallery.showLink`: Boolean, default false
      - `service_gallery.linkLabel`: String, default "View service"
      - `service_gallery.imageAspectRatio`: String, default "1/1"
      - `service_gallery.imageHeight`: Number, default 260
      - `service_gallery.imageRadius`: Number, default 20
      - `service_gallery.imageObjectFit`: String, default "cover"
      - `service_gallery.cardBackgroundColor`: String, default "var(--ion-card-background, #ffffff)"
      - `service_gallery.cardBorderRadius`: Number, default 28
      - `service_gallery.cardBorderWidth`: Number, default 0
      - `service_gallery.cardBorderColor`: String, default "transparent"
      - `service_gallery.cardPaddingX`: Number, default 20
      - `service_gallery.cardPaddingY`: Number, default 20
      - `service_gallery.gap`: Number, default 24
      - `service_gallery.boxShadowColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.22)"
      - `service_gallery.boxShadowOffsetY`: Number, default 20
      - `service_gallery.boxShadowBlurRadius`: Number, default 40
      - `service_gallery.boxShadowSpreadRadius`: Number, default -22
      - `service_gallery.fieldBackground`: String, default "transparent"
      - `service_gallery.hairlineColor`: String, default "transparent"
      - `service_gallery.hairlineWidth`: Number, default 0
      - `service_gallery.rowRuleColor`: String, default "transparent"
      - `service_gallery.rowRuleWidth`: Number, default 0
      - `service_gallery.maxWidth`: Number, default 0
      - `service_gallery.scrimGradient`: String, default ""
      - `service_gallery.align`: String, default "left"
      - `service_gallery.accentColor`: String, default "var(--ion-color-primary, #3880ff)"
      - `service_gallery.titleColor`: String, default "var(--ion-text-color, #1e293b)"
      - `service_gallery.titleFontSize`: String, default "1em"
      - `service_gallery.titleFontWeight`: String, default "700"
      - `service_gallery.titleFontFamily`: String, default ""
      - `service_gallery.titleTransform`: String, default "none"
      - `service_gallery.priceColor`: String, default "var(--ion-color-primary, #3880ff)"
      - `service_gallery.priceFontSize`: String, default "0.94em"
      - `service_gallery.priceFontWeight`: String, default "600"
      - `service_gallery.priceFontFamily`: String, default ""
      - `service_gallery.priceFontStyle`: String, default "normal"
      - `service_gallery.metaColor`: String, default "rgba(var(--ion-text-color-rgb, 30,41,59), 0.66)"
      - `service_gallery.titlePosition`: String, default "bottom"
      - `service_gallery.imageSide`: String, default "left"
      - `service_gallery.imagePadding`: Number, default 0
      - `service_gallery.boxShadowOffsetX`: Number, default 0
      - `service_gallery.descFontSize`: String, default "13px"
      - `service_gallery.descFontWeight`: String, default "400"
      - `service_gallery.animation`: String, default "none"
      - `service_gallery.animationDuration`: Number, default 0
      - `service_gallery.animationDelay`: Number, default 0
      - `service_gallery.showCustomFields`: Boolean, default false
      - `service_gallery.cardDesign`: String, default ""
      - `service_gallery.carouselLoop`: Boolean, default false
      - `service_gallery.carouselControls`: Boolean, default false
      - `service_gallery.customCss`: String, default ""
      - `blog_gallery.design_layout`: String, default ""
      - `blog_gallery.mode`: String, default "card"
      - `blog_gallery.columns`: Number, default 3
      - `blog_gallery.leadLayout`: String, default "beside"
      - `blog_gallery.leadWidth`: Number, default 58
      - `blog_gallery.placeholderItems`: Object[]
      - `blog_gallery.placeholderPlateColor`: String, default ""
      - `blog_gallery.showTitle`: Boolean, default true
      - `blog_gallery.showExcerpt`: Boolean, default false
      - `blog_gallery.showDate`: Boolean, default true
      - `blog_gallery.showImage`: Boolean, default true
      - `blog_gallery.showLink`: Boolean, default false
      - `blog_gallery.linkLabel`: String, default "Read more"
      - `blog_gallery.showCustomFields`: Boolean, default false
      - `blog_gallery.imageAspectRatio`: String, default "1/1"
      - `blog_gallery.imageHeight`: Number, default 260
      - `blog_gallery.imageRadius`: Number, default 20
      - `blog_gallery.imageObjectFit`: String, default "cover"
      - `blog_gallery.imagePadding`: Number, default 0
      - `blog_gallery.imageSide`: String, default "left"
      - `blog_gallery.cardBackgroundColor`: String, default "var(--ion-card-background, #ffffff)"
      - `blog_gallery.cardBorderRadius`: Number, default 28
      - `blog_gallery.cardBorderWidth`: Number, default 0
      - `blog_gallery.cardBorderColor`: String, default "transparent"
      - `blog_gallery.cardPaddingX`: Number, default 20
      - `blog_gallery.cardPaddingY`: Number, default 20
      - `blog_gallery.gap`: Number, default 24
      - `blog_gallery.boxShadowColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.22)"
      - `blog_gallery.boxShadowOffsetX`: Number, default 0
      - `blog_gallery.boxShadowOffsetY`: Number, default 20
      - `blog_gallery.boxShadowBlurRadius`: Number, default 40
      - `blog_gallery.boxShadowSpreadRadius`: Number, default -22
      - `blog_gallery.fieldBackground`: String, default "transparent"
      - `blog_gallery.hairlineColor`: String, default "transparent"
      - `blog_gallery.hairlineWidth`: Number, default 0
      - `blog_gallery.rowRuleColor`: String, default "transparent"
      - `blog_gallery.rowRuleWidth`: Number, default 0
      - `blog_gallery.maxWidth`: Number, default 0
      - `blog_gallery.scrimGradient`: String, default ""
      - `blog_gallery.align`: String, default "left"
      - `blog_gallery.accentColor`: String, default "var(--ion-color-primary, #3880ff)"
      - `blog_gallery.titleColor`: String, default "var(--ion-text-color, #1e293b)"
      - `blog_gallery.titleFontSize`: String, default "1em"
      - `blog_gallery.titleFontWeight`: String, default "700"
      - `blog_gallery.titleFontFamily`: String, default ""
      - `blog_gallery.titleTransform`: String, default "none"
      - `blog_gallery.dateColor`: String, default "var(--ion-color-primary, #3880ff)"
      - `blog_gallery.dateFontSize`: String, default "0.81em"
      - `blog_gallery.dateFontWeight`: String, default "600"
      - `blog_gallery.dateFontFamily`: String, default ""
      - `blog_gallery.dateFontStyle`: String, default "normal"
      - `blog_gallery.metaColor`: String, default "rgba(var(--ion-text-color-rgb, 30,41,59), 0.66)"
      - `blog_gallery.excerptFontSize`: String, default "0.88em"
      - `blog_gallery.excerptFontWeight`: String, default "400"
      - `blog_gallery.excerptFontFamily`: String, default ""
      - `blog_gallery.titlePosition`: String, default "bottom"
      - `blog_gallery.animation`: String, default "none"
      - `blog_gallery.animationDuration`: Number, default 0
      - `blog_gallery.animationDelay`: Number, default 0
      - `blog_gallery.cardDesign`: String, default ""
      - `blog_gallery.customCss`: String, default ""
      - `multi_booking.layout`: String, default "chips"
      - `multi_booking.columns`: Number, default 3
      - `multi_booking.pickerBackground`: String, default "transparent"
      - `multi_booking.gap`: Number, default 10
      - `multi_booking.align`: String, default "left"
      - `multi_booking.showPrice`: Boolean, default false
      - `multi_booking.showDuration`: Boolean, default false
      - `multi_booking.showDescription`: Boolean, default false
      - `multi_booking.showImage`: Boolean, default false
      - `multi_booking.itemPaddingX`: Number, default 18
      - `multi_booking.itemPaddingY`: Number, default 10
      - `multi_booking.itemRadius`: Number, default 100
      - `multi_booking.itemBorderWidth`: Number, default 0
      - `multi_booking.itemBorderColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.12)"
      - `multi_booking.itemBackground`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.10)"
      - `multi_booking.itemTextColor`: String, default "var(--ion-text-color, inherit)"
      - `multi_booking.itemSubTextColor`: String, default "var(--ion-color-medium, rgba(0,0,0,0.55))"
      - `multi_booking.selectedBackground`: String, default "var(--ion-color-primary)"
      - `multi_booking.selectedTextColor`: String, default "var(--ion-color-primary-contrast, #ffffff)"
      - `multi_booking.selectedSubTextColor`: String, default "rgba(var(--ion-color-primary-contrast-rgb, 255,255,255), 0.75)"
      - `multi_booking.selectedBorderColor`: String, default "var(--ion-color-primary)"
      - `multi_booking.hoverBackground`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.18)"
      - `multi_booking.titleFontSize`: Number, default 14
      - `multi_booking.titleFontWeight`: String, default "500"
      - `multi_booking.subFontSize`: Number, default 13
      - `multi_booking.dividerColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.12)"
      - `multi_booking.dividerWidth`: Number, default 1
      - `multi_booking.autoScroll`: Boolean, default true
      - `multi_booking.cardBackground`: String, default "var(--ion-card-color, #ffffff)"
      - `multi_booking.cardRadius`: Number, default 28
      - `multi_booking.cardBorderWidth`: Number, default 0
      - `multi_booking.cardBorderColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.12)"
      - `multi_booking.cardMaxWidth`: Number, default 820
      - `multi_booking.cardLayout`: String, default "stack"
      - `multi_booking.splitWidth`: Number, default 340
      - `multi_booking.shadowColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.18)"
      - `multi_booking.shadowOffsetY`: Number, default 20
      - `multi_booking.shadowBlur`: Number, default 40
      - `multi_booking.shadowSpread`: Number, default -22
      - `multi_booking.sectionPaddingX`: Number, default 40
      - `multi_booking.sectionPaddingY`: Number, default 32
      - `multi_booking.sectionDividerColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.12)"
      - `multi_booking.sectionDividerWidth`: Number, default 1
      - `multi_booking.showStepLabels`: Boolean, default true
      - `multi_booking.serviceStepLabel`: String, default "Step 1 — Service"
      - `multi_booking.dayStepLabel`: String, default "Step 2 — Day"
      - `multi_booking.staffStepLabel`: String, default "Step 3 — Staff member"
      - `multi_booking.timeStepLabel`: String, default "Step 4 — Time"
      - `multi_booking.stepLabelColor`: String, default "var(--ion-color-medium, rgba(0,0,0,0.55))"
      - `multi_booking.stepLabelSize`: Number, default 13
      - `multi_booking.stepLabelWeight`: String, default "700"
      - `multi_booking.stepLabelSpacing`: Number, default 0.04
      - `multi_booking.stepLabelUppercase`: Boolean, default true
      - `multi_booking.dayCount`: Number, default 6
      - `multi_booking.dayLayout`: String, default "strip"
      - `multi_booking.dayBorderColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.15)"
      - `multi_booking.dayShape`: String, default "circle"
      - `multi_booking.daySize`: Number, default 64
      - `multi_booking.dayGap`: Number, default 10
      - `multi_booking.dayShowWeekday`: Boolean, default false
      - `multi_booking.dayBackground`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.10)"
      - `multi_booking.dayTextColor`: String, default "var(--ion-text-color, inherit)"
      - `multi_booking.daySelectedBackground`: String, default "var(--ion-color-primary)"
      - `multi_booking.daySelectedTextColor`: String, default "var(--ion-color-primary-contrast, #ffffff)"
      - `multi_booking.dayDisabledOpacity`: Number, default 0.35
      - `multi_booking.timeGap`: Number, default 10
      - `multi_booking.timeRadius`: Number, default 100
      - `multi_booking.timeBorderWidth`: Number, default 0
      - `multi_booking.timeBorderColor`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.15)"
      - `multi_booking.timeSelectedBorderColor`: String, default "var(--ion-color-primary)"
      - `multi_booking.timePaddingX`: Number, default 18
      - `multi_booking.timePaddingY`: Number, default 10
      - `multi_booking.timeBackground`: String, default "rgba(var(--ion-color-primary-rgb, 56,128,255), 0.10)"
      - `multi_booking.timeTextColor`: String, default "var(--ion-text-color, inherit)"
      - `multi_booking.timeSelectedBackground`: String, default "var(--ion-color-primary)"
      - `multi_booking.timeSelectedTextColor`: String, default "var(--ion-color-primary-contrast, #ffffff)"
      - `multi_booking.buttonText`: String, default "Confirm my booking"
      - `multi_booking.buttonBackground`: String, default "var(--ion-color-primary)"
      - `multi_booking.buttonTextColor`: String, default "var(--ion-color-primary-contrast, #ffffff)"
      - `multi_booking.buttonRadius`: Number, default 100
      - `multi_booking.buttonPaddingY`: Number, default 16
      - `multi_booking.buttonFontSize`: Number, default 15
      - `multi_booking.buttonFontWeight`: String, default "700"
      - `multi_booking.buttonFullWidth`: Boolean, default true
      - `blogposts`: ObjectId[], → BlogPost
      - `videos`: ObjectId[], → Video
      - `price_plans`: ObjectId[], → PricePlan
      - `href`: String, default "#"
      - `router_link`: String
      - `buttonOptions.fill`: String
      - `buttonOptions.expand`: String
      - `buttonOptions.size`: String
      - `buttonOptions.color`: String
      - `buttonOptions.class`: String
      - `buttonOptions.customColor`: String
      - `buttonOptions.customColorContrast`: String
      - `buttonOptions.customColorHover`: String
      - `buttonOptions.hoverRoll`: Boolean
      - `buttonOptions.fontWeight`: Number
      - `buttonOptions.letterSpacing`: Number
      - `buttonOptions.fontSize`: Number, default 14
      - `buttonOptions.borderRadius`: Number, default 6
      - `buttonOptions.customColorA`: String
      - `buttonOptions.customColorB`: String
      - `buttonOptions.customColorC`: String
      - `buttonOptions.custom_measurements`: Boolean, default false
      - `buttonOptions.height.value`: String
      - `buttonOptions.height.unit`: String
      - `buttonOptions.width.value`: String
      - `buttonOptions.width.unit`: String
      - `carousel.showArrows`: Boolean, default true
      - `carousel.showIndicators`: Boolean, default true
      - `carousel.height`: Number, default 90
      - `carousel.automaticHeight`: Boolean, default true
      - `carousel.carouselMedias`: Object[]
      - `map`: String
      - `contactForm.name`: String
      - `contactForm.email`: String
      - `contactForm.subject`: String
      - `contactForm.body`: String
      - `contactForm.button`: String
      - `contactForm.hideNewsletter`: Boolean
      - `contactForm.placeholders.name`: String
      - `contactForm.placeholders.email`: String
      - `contactForm.placeholders.subject`: String
      - `contactForm.placeholders.body`: String
      - `contactForm.style.backgroundColor`: String
      - `contactForm.style.textColor`: String
      - `contactForm.style.accentColor`: String
      - `contactForm.style.border.color`: String
      - `contactForm.style.border.width.value`: Mixed
      - `contactForm.style.border.width.unit`: String
      - `contactForm.style.border.style`: String
      - `contactForm.style.borderRadius.value`: Mixed
      - `contactForm.style.borderRadius.unit`: String
      - `contactForm.style.padding.value`: Mixed
      - `contactForm.style.padding.unit`: String
      - `contactForm.style.labelFontSize.value`: Mixed
      - `contactForm.style.labelFontSize.unit`: String
      - `contactForm.style.consentFontSize.value`: Mixed
      - `contactForm.style.consentFontSize.unit`: String
      - `contactForm.style.inputBackgroundColor`: String
      - `contactForm.style.inputTextColor`: String
      - `contactForm.style.placeholderColor`: String
      - `contactForm.style.inputFocusColor`: String
      - `contactForm.style.inputFontSize.value`: Mixed
      - `contactForm.style.inputFontSize.unit`: String
      - `contactForm.style.inputPadding.value`: Mixed
      - `contactForm.style.inputPadding.unit`: String
      - `contactForm.style.textareaMinHeight.value`: Mixed
      - `contactForm.style.textareaMinHeight.unit`: String
      - `contactForm.style.inputMargin.value`: Mixed
      - `contactForm.style.inputMargin.unit`: String
      - `contactForm.style.inputBorder.color`: String
      - `contactForm.style.inputBorder.width.value`: Mixed
      - `contactForm.style.inputBorder.width.unit`: String
      - `contactForm.style.inputBorder.style`: String
      - `contactForm.style.inputBorderRadius.value`: Mixed
      - `contactForm.style.inputBorderRadius.unit`: String
      - `contactForm.style.buttonBackgroundColor`: String
      - `contactForm.style.buttonTextColor`: String
      - `contactForm.style.buttonAlign`: String
      - `contactForm.style.buttonFontSize.value`: Mixed
      - `contactForm.style.buttonFontSize.unit`: String
      - `contactForm.style.buttonBorder.color`: String
      - `contactForm.style.buttonBorder.width.value`: Mixed
      - `contactForm.style.buttonBorder.width.unit`: String
      - `contactForm.style.buttonBorder.style`: String
      - `contactForm.style.buttonBorderRadius.value`: Mixed
      - `contactForm.style.buttonBorderRadius.unit`: String
      - `contactForm.style.buttonWidth.value`: Mixed
      - `contactForm.style.buttonWidth.unit`: String
      - `contactForm.additional`: Object[]
      - `instagram.columns`: Number
      - `instagram.first`: Number
      - `instagram.skip`: Number
      - `instagram.showInfo`: Boolean
      - `instagram.gridType`: Number
      - `instagram.profile`: String
      - `facebook.url`: String
      - `facebook.width`: String
      - `facebook.height`: String
      - `animation`: String, default "none"
      - `animationDuration`: Number, default 0
      - `animationStrength`: Number
      - `parallaxEnabled`: Boolean
      - `parallaxAmount`: Number
      - `parallaxZoom`: Number
      - `textReveal`: String
      - `animationDelay`: Number, default 0
      - `animationReplay`: String, default "once"
      - `animationEasing`: String, default "power3.out"
      - `animationBreakpoint`: String, default "all"
      - `scrollScrubEnabled`: Boolean, default false
      - `scrollScrubAxis`: String, default "y"
      - `scrollScrubDistance`: Number, default 80
      - `motionOrigin`: String, default "center center"
      - `hoverEnabled`: Boolean, default false
      - `hoverEffect`: String, default "none"
      - `hoverDuration`: Number, default 240
      - `hoverIntensity`: Number, default 4
      - `continuousEnabled`: Boolean, default false
      - `continuousEffect`: String, default "none"
      - `continuousDuration`: Number, default 2400
      - `continuousIntensity`: Number, default 4
      - `statefulEnabled`: Boolean, default false
      - `statefulEffect`: String, default "none"
      - `statefulDuration`: Number, default 260
      - `statefulStagger`: Number, default 60
      - `transform.enabled`: Boolean, default false
      - `transform.translateX.value`: Number, default 0
      - `transform.translateX.unit`: String, default "px"
      - `transform.translateY.value`: Number, default 0
      - `transform.translateY.unit`: String, default "px"
      - `transform.scaleX`: Number, default 1
      - `transform.scaleY`: Number, default 1
      - `transform.rotate`: Number, default 0
      - `transform.rotateX`: Number, default 0
      - `transform.rotateY`: Number, default 0
      - `transform.skewX`: Number, default 0
      - `transform.skewY`: Number, default 0
      - `transform.perspective`: Number, default 0
      - `transform.originX`: String, default "center"
      - `transform.originY`: String, default "center"
      - `opacity`: Mixed
      - `transitionDuration`: Mixed
      - `transitionEasing`: String
      - `states`: Mixed
      - `useIframe`: Boolean, default false
      - `popup.text`: String
      - `popup.backgroundColor`: String
      - `popup.fullScreen`: Boolean, default false
      - `popup.about_page`: ObjectId, → AboutPage
      - `custom_component.html`: String
      - `custom_component.compiled_html`: String
      - `custom_component.css`: String
      - `custom_component.compiled_css`: String
      - `custom_component.js`: String
      - `custom_component.iframe`: Boolean, default false
      - `custom_component.variables`: Object[]
      - `hoverable_media.before_hover_media.media`: ObjectId, → Media
      - `hoverable_media.before_hover_media.gcs_url`: String
      - `hoverable_media.before_hover_media.vimeo_uri`: String
      - `hoverable_media.before_hover_media.vimeo_picture`: String
      - `hoverable_media.before_hover_media.youtube_url`: String
      - `hoverable_media.before_hover_media.vimeo_url`: String
      - `hoverable_media.before_hover_media.href`: String
      - `hoverable_media.before_hover_media.router_link`: String
      - `hoverable_media.before_hover_media.thumbnail`: String
      - `hoverable_media.before_hover_media.autoplay`: Boolean, default false
      - `hoverable_media.before_hover_media.loop`: Boolean, default false
      - `hoverable_media.before_hover_media.show_controls`: Boolean, default true
      - `hoverable_media.after_hover_media.media`: ObjectId, → Media
      - `hoverable_media.after_hover_media.gcs_url`: String
      - `hoverable_media.after_hover_media.vimeo_uri`: String
      - `hoverable_media.after_hover_media.vimeo_picture`: String
      - `hoverable_media.after_hover_media.youtube_url`: String
      - `hoverable_media.after_hover_media.vimeo_url`: String
      - `hoverable_media.after_hover_media.href`: String
      - `hoverable_media.after_hover_media.router_link`: String
      - `hoverable_media.after_hover_media.thumbnail`: String
      - `hoverable_media.after_hover_media.autoplay`: Boolean, default false
      - `hoverable_media.after_hover_media.loop`: Boolean, default false
      - `hoverable_media.after_hover_media.show_controls`: Boolean, default true
      - `collapsible_container`: Object[]
      - `collapsible_container_settings.enable_customization`: Boolean, default false
      - `collapsible_container_settings.title_color`: String
      - `collapsible_container_settings.title_color_checked`: String
      - `collapsible_container_settings.title_background`: String
      - `collapsible_container_settings.title_background_checked`: String
      - `collapsible_container_settings.title_font_size`: String
      - `collapsible_container_settings.title_border_radius`: String
      - `collapsible_container_settings.title_border_color`: String
      - `collapsible_container_settings.title_border_width`: String
      - `collapsible_container_settings.body_color`: String
      - `collapsible_container_settings.body_background`: String
      - `collapsible_container_settings.body_border_radius`: String
      - `collapsible_container_settings.body_border_color`: String
      - `collapsible_container_settings.body_border_width`: String
      - `collapsible_container_settings.body_font_size`: String
      - `collapsible_container_settings.icon_color`: String
      - `collapsible_container_settings.icon_glyph_color`: String
      - `collapsible_container_settings.icon_background_color`: String
      - `quiz.title`: String
      - `quiz.header`: String
      - `quiz.questions`: Object[]
      - `quiz.score_results`: Object[]
      - `quiz.show_right_answers`: Boolean, default false
      - `quiz.mutliple_choice`: Boolean, default false
      - `partners.defaultCollection`: String, default ""
      - `partners.defaultView`: String, default "locations"
      - `scroll_sequence.media`: ObjectId, → Media
      - `scroll_sequence.scroll_length`: Number, default 3
      - `scroll_sequence.fit`: String, one of cover | contain, default "cover"
      - `scroll_sequence.background_color`: String, default "#000000"
      - `scroll_sequence.overlay_color`: String, default ""
      - `scroll_sequence.overlay_opacity`: Number, default 0
      - `scroll_sequence.rows_over`: Boolean, default true
      - `scroll_sequence.rows_effect`: String, one of slide | pop, default "slide"
      - `scroll_sequence.snap_points`: Number[]
      - `scroll_sequence.snap_scroll_speed`: Number, default 1.5
      - `scroll_sequence.snap_down`: Boolean, default true
      - `scroll_sequence.snap_up`: Boolean, default false
      - `scroll_sequence.stop_momentum`: Boolean, default false
      - `smart_search.placeholder`: String, default ""
      - `smart_search.kinds`: String[]
      - `smart_search.collections`: String[]
      - `smart_search.items`: Object[]
      - `smart_search.showKindFilter`: Boolean, default true
      - `smart_search.showPriceFilter`: Boolean, default false
      - `smart_search.showDateFilter`: Boolean, default false
      - `smart_search.showSort`: Boolean, default true
      - `smart_search.dateMode`: String, one of created | updated | availability, default "created"
      - `smart_search.layout`: String, one of grid | list, default "grid"
      - `smart_search.columns`: Number, default 3
      - `smart_search.perPage`: Number, default 12
      - `smart_search.instant`: Boolean, default true
      - `smart_search.showImages`: Boolean, default true
      - `smart_search.showPrices`: Boolean, default true
      - `smart_search.showInitial`: Boolean, default false
      - `smart_search.showAnswer`: Boolean, default false
      - `countdown.targetDate`: String, default ""
      - `countdown.expiredText`: String, default "Offer has ended"
      - `countdown.hideOnExpire`: Boolean, default false
      - `countdown.textColor`: String, default ""
      - `countdown.boxColor`: String, default ""
      - `countdown.align`: String, one of  | left | center | right, default ""
      - `swiper.slides`: Object[]
      - `swiper.autoplay`: Boolean, default false
      - `swiper.loop`: Boolean, default false
      - `swiper.show_controls`: Boolean, default true
      - `swiper.dynamic_bullets`: Boolean, default false
      - `swiper.pagination`: Boolean, default true
      - `dynamicField`: String
      - `useDynamicField`: Boolean, default false
      - `text_auto_slider.speed`: Number
      - `text_auto_slider.aspect_ratio`: String
      - `text_auto_slider.text_color`: String
      - `text_auto_slider.background_color`: String
      - `text_auto_slider.delimiter_color`: String
      - `text_auto_slider.direction`: String
      - `text_auto_slider.width`: Number
      - `text_auto_slider.font_size`: Number
      - `text_auto_slider.text_transform`: String, one of none | uppercase, default "none"
      - `text_auto_slider.texts`: Object[]
      - `image_auto_slider.speed`: Number
      - `image_auto_slider.aspect_ratio`: String
      - `image_auto_slider.direction`: String
      - `image_auto_slider.width`: Number
      - `image_auto_slider.medias`: Object[]
      - `image_dragger.aspect_ratio`: String
      - `image_dragger.media_1.media`: ObjectId, → Media
      - `image_dragger.media_1.gcs_url`: String
      - `image_dragger.media_1.vimeo_uri`: String
      - `image_dragger.media_1.vimeo_picture`: String
      - `image_dragger.media_1.vimeo_url`: String
      - `image_dragger.media_1.youtube_url`: String
      - `image_dragger.media_2.media`: ObjectId, → Media
      - `image_dragger.media_2.gcs_url`: String
      - `image_dragger.media_2.vimeo_uri`: String
      - `image_dragger.media_2.vimeo_picture`: String
      - `image_dragger.media_2.vimeo_url`: String
      - `image_dragger.media_2.youtube_url`: String
      - `image_dragger.media_2.title`: String
      - `image_dragger.media_2.body`: String
      - `image_dragger.media_2.textColor`: String
      - `image_dragger.media_2.href`: String
      - `image_dragger.media_2.router_link`: String
      - `_id`: ObjectId
    - `sections`: Object[]
      - `position`: String, one of static | relative | absolute | fixed | sticky, default "static"
      - `overflow`: String, one of visible | hidden | scroll | auto, default "visible"
      - `editorName`: String
      - `top.value`: String, default "0"
      - `top.unit`: String, default "px"
      - `top.mobile_value`: String, default ""
      - `top.mobile_unit`: String, default "px"
      - `left.value`: String, default "0"
      - `left.unit`: String, default "px"
      - `left.mobile_value`: String, default ""
      - `left.mobile_unit`: String, default "px"
      - `right.value`: String, default "0"
      - `right.unit`: String, default "px"
      - `right.mobile_value`: String, default ""
      - `right.mobile_unit`: String, default "px"
      - `bottom.value`: String, default "0"
      - `bottom.unit`: String, default "px"
      - `bottom.mobile_value`: String, default ""
      - `bottom.mobile_unit`: String, default "px"
      - `display`: String, one of block | inline | flex, default "block"
      - `flex_direction`: String, one of row | row-reverse | column | column-reverse, default "row"
      - `flex_direction_mobile`: String, default ""
      - `backdropFilter`: String, default ""
      - `justify`: String, one of start | end | center | space-between | space-around | stretch, default "start"
      - `justify_mobile`: String, default ""
      - `align`: String, one of start | end | center | space-between | space-around | stretch, default "start"
      - `align_mobile`: String, default ""
      - `animation`: String, default "none"
      - `animationDuration`: Number, default 0
      - `animationStrength`: Number
      - `parallaxEnabled`: Boolean
      - `parallaxAmount`: Number
      - `parallaxZoom`: Number
      - `animationDelay`: Number, default 0
      - `animationReplay`: String, default "once"
      - `animationEasing`: String, default "power3.out"
      - `animationBreakpoint`: String, default "all"
      - `scrollScrubEnabled`: Boolean, default false
      - `scrollScrubAxis`: String, default "y"
      - `scrollScrubDistance`: Number, default 80
      - `motionOrigin`: String, default "center center"
      - `hoverEnabled`: Boolean, default false
      - `hoverEffect`: String, default "none"
      - `hoverDuration`: Number, default 240
      - `hoverIntensity`: Number, default 4
      - `continuousEnabled`: Boolean, default false
      - `continuousEffect`: String, default "none"
      - `continuousDuration`: Number, default 2400
      - `continuousIntensity`: Number, default 4
      - `statefulEnabled`: Boolean, default false
      - `statefulEffect`: String, default "none"
      - `statefulDuration`: Number, default 260
      - `statefulStagger`: Number, default 60
      - `transform.enabled`: Boolean, default false
      - `transform.translateX.value`: Number, default 0
      - `transform.translateX.unit`: String, default "px"
      - `transform.translateY.value`: Number, default 0
      - `transform.translateY.unit`: String, default "px"
      - `transform.scaleX`: Number, default 1
      - `transform.scaleY`: Number, default 1
      - `transform.rotate`: Number, default 0
      - `transform.rotateX`: Number, default 0
      - `transform.rotateY`: Number, default 0
      - `transform.skewX`: Number, default 0
      - `transform.skewY`: Number, default 0
      - `transform.perspective`: Number, default 0
      - `transform.originX`: String, default "center"
      - `transform.originY`: String, default "center"
      - `opacity`: Mixed
      - `transitionDuration`: Mixed
      - `transitionEasing`: String
      - `states`: Mixed
      - `hoverGroup`: Boolean
      - `flex_wrap`: String, one of nowrap | wrap | wrap-reverse, default "wrap"
      - `flex_wrap_mobile`: String, default ""
      - `width.value`: String, default ""
      - `width.unit`: String, default "px"
      - `width.mobile_value`: String, default ""
      - `width.mobile_unit`: String, default "px"
      - `min_width.value`: String, default ""
      - `min_width.unit`: String, default "px"
      - `min_width.mobile_value`: String, default ""
      - `min_width.mobile_unit`: String, default "px"
      - `max_width.value`: String, default ""
      - `max_width.unit`: String, default "px"
      - `max_width.mobile_value`: String, default ""
      - `max_width.mobile_unit`: String, default "px"
      - `height.value`: String, default ""
      - `height.unit`: String, default "px"
      - `height.mobile_value`: String, default ""
      - `height.mobile_unit`: String, default "px"
      - `min_height.value`: String, default ""
      - `min_height.unit`: String, default "px"
      - `min_height.mobile_value`: String, default ""
      - `min_height.mobile_unit`: String, default "px"
      - `max_height.value`: String, default ""
      - `max_height.unit`: String, default "px"
      - `max_height.mobile_value`: String, default ""
      - `max_height.mobile_unit`: String, default "px"
      - `object_fit`: String
      - `aspect_ratio.custom`: Boolean, default false
      - `aspect_ratio.ratio`: String, default ""
      - `margin_left.value`: String, default "0"
      - `margin_left.unit`: String, default "px"
      - `margin_left.mobile_value`: String, default ""
      - `margin_left.mobile_unit`: String, default "px"
      - `margin_right.value`: String, default "0"
      - `margin_right.unit`: String, default "px"
      - `margin_right.mobile_value`: String, default ""
      - `margin_right.mobile_unit`: String, default "px"
      - `margin_top.value`: String, default "0"
      - `margin_top.unit`: String, default "px"
      - `margin_top.mobile_value`: String, default ""
      - `margin_top.mobile_unit`: String, default "px"
      - `margin_bottom.value`: String, default "0"
      - `margin_bottom.unit`: String, default "px"
      - `margin_bottom.mobile_value`: String, default ""
      - `margin_bottom.mobile_unit`: String, default "px"
      - `padding_left.value`: String, default "0"
      - `padding_left.unit`: String, default "px"
      - `padding_left.mobile_value`: String, default ""
      - `padding_left.mobile_unit`: String, default "px"
      - `padding_right.value`: String, default "0"
      - `padding_right.unit`: String, default "px"
      - `padding_right.mobile_value`: String, default ""
      - `padding_right.mobile_unit`: String, default "px"
      - `padding_top.value`: String, default "0"
      - `padding_top.unit`: String, default "px"
      - `padding_top.mobile_value`: String, default ""
      - `padding_top.mobile_unit`: String, default "px"
      - `padding_bottom.value`: String, default "0"
      - `padding_bottom.unit`: String, default "px"
      - `padding_bottom.mobile_value`: String, default ""
      - `padding_bottom.mobile_unit`: String, default "px"
      - `border_left.style`: String, default "none"
      - `border_left.color`: String
      - `border_left.width.value`: String, default "0"
      - `border_left.width.unit`: String, default "px"
      - `border_right.style`: String, default "none"
      - `border_right.color`: String
      - `border_right.width.value`: String, default "0"
      - `border_right.width.unit`: String, default "px"
      - `border_top.style`: String, default "none"
      - `border_top.color`: String
      - `border_top.width.value`: String, default "0"
      - `border_top.width.unit`: String, default "px"
      - `border_bottom.style`: String, default "none"
      - `border_bottom.color`: String
      - `border_bottom.width.value`: String, default "0"
      - `border_bottom.width.unit`: String, default "px"
      - `border_top_left_radius.value`: String, default "0"
      - `border_top_left_radius.unit`: String, default "px"
      - `border_top_right_radius.value`: String, default "0"
      - `border_top_right_radius.unit`: String, default "px"
      - `border_bottom_left_radius.value`: String, default "0"
      - `border_bottom_left_radius.unit`: String, default "px"
      - `border_bottom_right_radius.value`: String, default "0"
      - `border_bottom_right_radius.unit`: String, default "px"
      - `border_radius.value`: String, default "0"
      - `border_radius.unit`: String, default "px"
      - `specific_border_radius`: Boolean, default false
      - `box_shadows`: Object[]
      - `backgroundColor`: String
      - `backgroundGradient.enabled`: Boolean, default false
      - `backgroundGradient.type`: String
      - `backgroundGradient.degrees`: Number
      - `backgroundGradient.radialShape`: String
      - `backgroundGradient.radialPosition`: String
      - `backgroundGradient.colors`: Object[]
      - `backgroundGradient.css`: String
      - `backgroundVideo`: String
      - `backgroundImage`: String
      - `backgroundSize`: String
      - `backgroundPosition`: String
      - `backgroundRepeat`: String
      - `showOnMobile`: Boolean, default true
      - `showOnDesktop`: Boolean, default true
      - `components`: Object[]
      - `sections`: Object[]
      - `_id`: ObjectId
    - `_id`: ObjectId
  - `ai_meta`: Mixed
  - `_id`: ObjectId
- `suitability_price_plans`: ObjectId[], → PricePlan
- `isSuitable`: Boolean
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### BlogPost

An article on the site's blog.

- `title`: String, required - Name of the blog post, required.
- `url`: String, required
- `static_url`: String
- `header`: String - Short excerpt or summary shown in listings and social previews, required.
- `description`: String, required - Full body text of the blog post in HTML format, required.
- `medias`: Object[]
  - `media`: ObjectId, → Media
  - `gcs_url`: String
  - `gcs_480_30_url`: String
  - `gcs_720_30_url`: String
  - `gcs_1080_30_url`: String
  - `duration`: Number
  - `thumbnail`: String
  - `vimeo_uri`: String
  - `vimeo_picture`: String
  - `vimeo_url`: String
  - `youtube_url`: String
  - `youtube_picture`: String
  - `_id`: ObjectId
- `published`: Boolean, default true - If true the post is visible on the site, default true.
- `owner_id`: ObjectId, → User
- `connected_account_owner_id`: ObjectId, → User
- `collection_id`: ObjectId, → Collection - Reference to a Collection model with name starting with 'blogpost/', used to categorise the post.
- `selected`: Boolean, default false - If true the post is highlighted/featured, default false.
- `custom_1`: String - Optional text field used for filtering.
- `custom_2`: String - Optional text field used for filtering.
- `custom_3`: String - Optional text field used for filtering.
- `about_page`: ObjectId, → AboutPage - Reference to AboutPage model, shown at the bottom of the post page.
- `suitability_price_plans`: ObjectId[], → PricePlan - A list of references to PricePlan model, marks post as suitable for users with those plans.
- `isSuitable`: Boolean
- `reference_id`: String
- `wildcard_url`: Mixed
- `likes`: ObjectId[], → User
- `views`: ObjectId[], → User
- `total_views`: Number, default 0
- `saves`: ObjectId[], → User
- `times_showed`: Number, default 0
- `requiresLogin`: Boolean, default false
- `requiresSubscription`: Boolean, default false
- `price_plan_ids`: String[] - A list of price plan id strings that grant access to this post.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### ChatMessage

- `chat_id`: ObjectId, required, → ChatHistory
- `role`: String, one of user | model
- `text`: String
- `target_url`: String
- `action`: String, one of RESPONSE | REDIRECT | OBJECT_RESPONSE | ERROR | CLARIFY | PROPOSED_ACTION
- `options`: String[]
- `data.object`: Mixed
- `data.object_type`: String
- `data.edit_summary`: Mixed
- `data.edit_diff`: Mixed
- `data.undo_record`: Mixed
- `data.hero_animation_offer`: Mixed
- `data.preview_state`: String
- `data.pending_credit_cost`: Number
- `data.target_restore`: Mixed
- `data.media_request`: Mixed
- `data.item_request`: Mixed
- `data.reference_request`: Mixed
- `data.section_options`: Mixed
- `data.section_blueprint`: Mixed
- `data.section_style_options`: Mixed
- `data.section_style_blueprint`: Mixed
- `data.section_role_picker`: Mixed
- `data.small_section_regeneration_options`: Mixed
- `data.small_section_regeneration_target`: Mixed
- `data.small_section_regeneration_prompt`: String
- `data.selected_small_section_action`: Mixed
- `data.component_regeneration_input`: Mixed
- `data.selected_section_role`: Mixed
- `data.selected_section_option`: Mixed
- `data.selected_section_style`: Mixed
- `data.section_options_target`: Mixed
- `data.section_options_prompt`: String
- `data.style_options_target`: Mixed
- `data.style_options_prompt`: String
- `data.previous_container`: Mixed
- `data.preserved_custom_components`: Mixed[]
- `data.selection_request`: Mixed
- `data.proposed_action`: Mixed
- `data.plan_result`: Mixed
- `data.parsed_intent`: Mixed
- `data.parsed_intent_version`: Number
- `data.parsed_intent_at`: Date
- `data.item_id`: ObjectId
- `data.design_archetype`: String
- `data.design_palette`: Mixed
- `data.out_of_credits`: Boolean
- `data.site_wide_edit`: Mixed
- `data.redesign`: Mixed
- `medias`: Object[]
  - `media`: ObjectId, → Media
  - `gcs_url`: String
  - `gemini_uri`: String
  - `gemini_interaction_id`: String
  - `_id`: ObjectId
- `products`: ObjectId[], → Product
- `services`: ObjectId[], → Service
- `blogposts`: ObjectId[], → BlogPost
- `programs`: ObjectId[], → Program
- `suggestions`: Object[]
  - `text`: String
  - `route`: String
  - `item_id`: ObjectId
  - `medias`: Mixed[]
  - `picture`: ObjectId
  - `_id`: ObjectId
- `is_system`: Boolean, default false
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### CustomPage

- `title`: String, required
- `url`: String, required
- `html_content`: String
- `css_content`: String
- `js_content`: String
- `_id`: ObjectId

### Meta

- `title`: String
- `url`: String, required
- `description`: String
- `site_name`: String
- `image`: ObjectId
- `_id`: ObjectId

### Product

An item the store sells, with its price, images, options and delivery details.

- `name`: String, required - Name of the product, required.
- `url`: String, required - Url of the product, it is automatically generated.
- `static_url`: String
- `ribbon`: String - A banner that appears on top of product, for example 'Sale', 'New', etc.
- `striked`: String - A striked text that appears next to price, useful for striking old price.
- `price`: Number, required - Price of the product - default 0 if not provided.
- `description`: String - Description of product in html format.
- `product_informations`: Object[] - Dropdown texts with additional information about the product.
  - `name`: String, required
  - `description`: String, required
  - `_id`: ObjectId
- `product_options`: Object[]
  - `name`: String, required
  - `options`: Object[]
    - `option`: String, required
    - `match_to_collection`: ObjectId, → Collection
    - `quantity`: Number
    - `badge`: String
    - `option_specific_media`: ObjectId, → Media
    - `_id`: ObjectId
  - `informations`: Object[]
    - `name`: String
    - `description`: String
    - `_id`: ObjectId
  - `_id`: ObjectId
- `published`: Boolean, default true - If true the product is visible in the site/app product pages - default true.
- `available_for_purchase`: Boolean, default true - If true the product can be purchased - default true.
- `medias`: Object[]
  - `media`: ObjectId, → Media
  - `gcs_url`: String
  - `gcs_480_30_url`: String
  - `gcs_720_30_url`: String
  - `gcs_1080_30_url`: String
  - `duration`: Number
  - `vimeo_uri`: String
  - `vimeo_picture`: String
  - `vimeo_url`: String
  - `youtube_url`: String
  - `youtube_picture`: String
  - `thumbnail`: String
  - `_id`: ObjectId
- `collection_id`: ObjectId, → Collection - Reference to the Collection model, it is the store where product is grouped.
- `grouping`: Object[] - References to the Collection model, they act as filters.
  - `collection_id`: ObjectId, → Collection
  - `_id`: ObjectId
- `owner_id`: ObjectId, required, → User
- `connected_account_owner_id`: ObjectId, → User
- `qty`: Number, default 1
- `origin_address`: String - Name of the address, taken from OriginAdress model, used for filtering.
- `selected`: Boolean, default false
- `custom_1`: String - Field used for filtering if additionalFieldsLocales are present.
- `custom_2`: String - Field used for filtering if additionalFieldsLocales are present.
- `custom_3`: String - Field used for filtering if additionalFieldsLocales are present.
- `about_page`: ObjectId, → AboutPage - Reference to AboutPage model, shown in product page.
- `is_digital`: Boolean, default false
- `digital_contents`: Object[]
  - `content`: ObjectId, → DigitalContent
  - `option`: String
  - `_id`: ObjectId
- `suitability_price_plans`: ObjectId[], → PricePlan - A list of references to PricePlan model, if user has any of the listed priceplan ids, then is Suitable is true.
- `isSuitable`: Boolean
- `brand`: String - Name of the product brand.
- `brand_logo`: ObjectId, → Media - Logo of the brand, reference to Media model.
- `weight`: Number, default 0 - Weight of product in kg.
- `height`: Number, default 0 - Weight of product in cm.
- `length`: Number, default 0 - Weight of product in cm.
- `width`: Number, default 0 - Weight of product in cm.
- `delivery_type`: String, default "parcel" - Used for delivery, default is parcel, allowed values are books, bulk, letter, parcel or parcel_letter.
- `reference_id`: String
- `secondary_cta.show`: Boolean, default false
- `secondary_cta.title`: String
- `secondary_cta.webUrl`: String
- `secondary_cta.appUrl`: String
- `wildcard_url`: Mixed
- `sku`: Mixed - Sku of product if available.
- `reviewed`: Boolean
- `approved`: Boolean
- `expired`: Boolean, default false
- `expires_on`: Date
- `expire_reminder_sent`: Boolean, default false
- `likes`: ObjectId[], → User
- `views`: ObjectId[], → User
- `total_views`: Number, default 0
- `saves`: ObjectId[], → User
- `times_showed`: Number, default 0
- `requiresLogin`: Boolean, default false
- `requiresSubscription`: Boolean, default false
- `price_plan_ids`: String[] - Reference to PricePlan ids.
- `linked_products`: Object[] - List of products that are shown for quick add to cart in the product page.
  - `product`: ObjectId, → Product
  - `option`: String
  - `_id`: ObjectId
- `attached_priceplans.name`: String
- `attached_priceplans.priceplans`: ObjectId[], → PricePlan
- `about_section`: ObjectId, → AboutPage - Reference to AboutPage model, shown below the add to cart button.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### Publish

- `web`: Boolean
- `ios`: Boolean
- `android`: Boolean
- `pwaSwitch`: String
- `android_app_id`: String
- `ios_app_id`: String
- `_id`: ObjectId

### Service

A bookable service: its duration, price, the staff who perform it and when it can be booked.

- `title`: String, required
- `url`: String, required
- `static_url`: String
- `header`: String - This is the subtitle of the service.
- `description`: String - This is the description of the service.
- `bookable`: Boolean, default true
- `expirable`: Boolean, default false
- `confirm_bookings`: Boolean, default false
- `show_notes`: Boolean, default false
- `minutes_delete_bookings`: Number - Number of minutes before the booking expires before being confirmed and is set 'expirable:true'.
- `medias`: Object[]
  - `media`: ObjectId, → Media
  - `gcs_url`: String
  - `gcs_480_30_url`: String
  - `gcs_720_30_url`: String
  - `gcs_1080_30_url`: String
  - `duration`: Number
  - `vimeo_uri`: String
  - `vimeo_picture`: String
  - `vimeo_url`: String
  - `youtube_url`: String
  - `youtube_picture`: String
  - `thumbnail`: String
  - `_id`: ObjectId
- `collection_id`: ObjectId, → Collection - Reference to the Collection model, it is the store where service is grouped.
- `price`: Number - The price of the service in the globally set currency.
- `online_payment`: Boolean, default false
- `book_by_date`: Boolean, default false
- `book_date_range`: Boolean, default false
- `start_date`: Date - Start date of allowed slots for booking.
- `end_date`: Date - End date of allowed slots for booking.
- `day_offset`: Number, default 0
- `is_recurring`: Boolean, default false
- `max_participants`: Number, required
- `session_duration`: Number, required
- `time_between_sessions`: Number, default 0
- `discount`: Object[]
  - `days`: Number
  - `per_total_price`: Number
  - `fixed_price`: Number
  - `per_day`: Number
  - `_id`: ObjectId
- `monday.active`: Boolean, default false
- `monday.hours`: Object[]
  - `start`: String
  - `end`: String
  - `_id`: ObjectId
- `tuesday.active`: Boolean, default false
- `tuesday.hours`: Object[]
  - `start`: String
  - `end`: String
  - `_id`: ObjectId
- `wednesday.active`: Boolean, default false
- `wednesday.hours`: Object[]
  - `start`: String
  - `end`: String
  - `_id`: ObjectId
- `thursday.active`: Boolean, default false
- `thursday.hours`: Object[]
  - `start`: String
  - `end`: String
  - `_id`: ObjectId
- `friday.active`: Boolean, default false
- `friday.hours`: Object[]
  - `start`: String
  - `end`: String
  - `_id`: ObjectId
- `saturday.active`: Boolean, default false
- `saturday.hours`: Object[]
  - `start`: String
  - `end`: String
  - `_id`: ObjectId
- `sunday.active`: Boolean, default false
- `sunday.hours`: Object[]
  - `start`: String
  - `end`: String
  - `_id`: ObjectId
- `address`: String - Where is the service performed.
- `staff_member`: ObjectId[], → User
- `owner_id`: ObjectId, → User
- `connected_account_owner_id`: ObjectId, → User
- `timezone`: Number, default false
- `choose_timezone`: Boolean
- `origin_address`: String
- `selected`: Boolean, default false
- `custom_1`: String - Field used for filtering if additionalFieldsLocales are present.
- `custom_2`: String - Field used for filtering if additionalFieldsLocales are present.
- `custom_3`: String - Field used for filtering if additionalFieldsLocales are present.
- `about_page`: ObjectId, → AboutPage
- `show_only_about_page`: Boolean, default false
- `suitability_price_plans`: ObjectId[], → PricePlan
- `isSuitable`: Boolean
- `booking_only`: Boolean - If the booking page should not show a header and footer.
- `dates_disabled`: String[]
- `dates_booked`: String[]
- `receipt_about_page`: ObjectId, → AboutPage
- `wildcard_url`: Mixed
- `locked_hours`: Date[]
- `reviewed`: Boolean
- `approved`: Boolean
- `likes`: ObjectId[], → User
- `views`: ObjectId[], → User
- `total_views`: Number, default 0
- `saves`: ObjectId[], → User
- `times_showed`: Number, default 0
- `analytics_locales.google_event_custom_title`: String, default "schedule"
- `analytics_locales.facebook_event_custom_title`: String, default "Schedule"
- `analytics_locales.tiktok_event_custom_title`: String
- `analytics_locales.linkedin_event_custom_title`: String
- `analytics_locales.chatgpt_event_custom_title`: String
- `email_text`: String - The text the client will receive inside the email.
- `calendar_description`: String - The text the client will see when adding the appointment inside their calendar.
- `requiresLogin`: Boolean, default false
- `requiresSubscription`: Boolean, default false
- `price_plan_ids`: String[]
- `calendar_filter`: Object[]
  - `collection_id`: ObjectId, → Collection
  - `_id`: ObjectId
- `locked_ranges`: Object[]
  - `start`: Date
  - `end`: Date
  - `_id`: ObjectId
- `connected_services`: ObjectId[], → Service
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### SiteMenu

A link in the site's menu.

- `title`: String - The menu link label shown to users, required.
- `url`: String - The link target.
- `position`: Number - Display order starting from 0, optional.
- `submenus`: Object[]
  - `title`: String
  - `url`: String
  - `position`: Number
  - `_id`: ObjectId
- `customDropdown`: Boolean, default false
- `customDropdownId`: ObjectId, → AboutPage
- `sideMenu`: Boolean, default false
- `sidemenus`: Object[]
  - `title`: String
  - `submenus`: Object[]
    - `title`: String
    - `url`: String
    - `position`: Number
    - `icon`: String
    - `img`: ObjectId, → Media
    - `submenus`: Object[]
      - `title`: String
      - `url`: String
      - `position`: Number
      - `icon`: String
      - `img`: ObjectId, → Media
      - `_id`: ObjectId
    - `_id`: ObjectId
  - `_id`: ObjectId
- `icon`: String - FontAwesome 5 class (e.g. "fas fa-home"), optional.
- `_id`: ObjectId
