# Builderly API reference

Base URL: http://smartman.max-media.io
Generated from the running server on 2026-11-09T17:13:29.513Z. 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

### Emails

The emails the site sends: their settings, templates, the texts added to order emails and bulk email campaigns.

#### Email

##### Retrieve the email
`GET /email`
The site's email texts, one per email type. Answers an array of every record.
- Access: Admin token
- Returns: the Email object.
- Status codes: 200, 401, 500
- Models: Email

##### List all emails
`GET /email/:type`
The texts of one email (welcome, confirmation...). 404 when the site has no texts for that email yet: the built-in wording is used.
- Access: Admin token
- Path:
  - `type` (string): Which email this is (welcome, confirmation, resetPassword, newsletter...).
- Returns: a list of Email objects.
- Status codes: 200, 401, 404, 500
- Models: Email

#### Email Template

##### Retrieve the email template
`GET /email-template`
The email templates. Answers an array of every record.
- Access: Admin token
- Returns: the EmailTemplate object.
- Status codes: 200, 401, 500
- Models: EmailTemplate

##### Create an email template
`POST /email-template`
Creates an email template. Answers 201 with no body.
- Access: Admin token
- Body:
  - `owner_id` (objectid): User.id of who made it.
- Returns: the EmailTemplate object.
- Status codes: 201, 400, 401, 500
- Models: EmailTemplate

##### Retrieve an email template
`GET /email-template/:id`
One email template. 404 when there is no such record.
- Access: Admin token
- Path:
  - `id` (string): The ID of the email template.
- Returns: the EmailTemplate object.
- Status codes: 200, 401, 404, 500
- Models: EmailTemplate

##### Update an email template
`PATCH /email-template/:id`
Updates an email template. Fields outside the allowed list are silently ignored. Answers the updated record, or 404.
- Access: Admin token
- Path:
  - `id` (string): The ID of the email template.
- Returns: the EmailTemplate object.
- Status codes: 200, 400, 401, 404, 500
- Models: EmailTemplate

##### Delete an email template
`DELETE /email-templates/:id`
Deletes an email template. Answers the deleted record, or 404. Records that point to it are not changed.
- Access: Admin token
- Path:
  - `id` (string): The ID of the email template.
- Returns: an empty answer on success.
- Status codes: 200, 401, 404, 500
- Models: EmailTemplate

#### AI builder

##### Modify an email setting with AI
`POST /modify-email-settings`
AI: works out which emails the owner wants changed and routes to update-email-settings-ai. Does no AI work itself: answers { action: 'REDIRECT', target_url, response } and the chat client calls target_url next with response as the prompt.
- Access: Admin token
- Body:
  - `prompt` (string): What the AI should do with the email setting, 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.
- Status codes: 200, 401, 500

##### Modify an email template with AI
`POST /modify-email-template`
Use it to design or create the visual look of your emails.
- Access: Admin token
- Body:
  - `prompt` (string): What the AI should do with the email template, 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.
- Status codes: 200, 401, 500

##### Modify a store email text with AI
`POST /modify-store-email-text`
Use it to write or customize the body message that appears inside order or subscription emails.
- Access: Admin token
- Body:
  - `prompt` (string): What the AI should do with the store email text, 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.
- Status codes: 200, 401, 500

##### Update an email setting with AI
`POST /update-email-settings-ai`
AI: rewrites the site's emails as the owner asked. Answers { action: 'JOB', async_job } at once; the admin polls GET /async-jobs/:id, and the result arrives in the chat as a message when the job ends. The AI work is charged to the account's credits.
- Access: Admin token
- Body:
  - `prompt` (string): What the AI should do with the email setting, 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.
  - `email_type` (string): The email setting's email type.
- Status codes: 200, 401, 500

##### Update an email template with AI
`POST /update-email-template-ai`
Update an email template from a prompt, written by the AI builder.
- Access: Admin token
- Body:
  - `prompt` (string): What the AI should do with the email template, 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 email template to change.
- Status codes: 200, 401, 500
- Models: ChatMessage

##### Update a store email text with AI
`POST /update-store-email-text-ai`
Update a store email text from a prompt, written by the AI builder.
- Access: Admin token
- Body:
  - `prompt` (string): What the AI should do with the store email text, 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 store email text to change.
- Status codes: 200, 401, 500
- Models: ChatMessage

## Objects

### ChatMessage

- `chat_id`: ObjectId, required, → ChatHistory - ChatHistory.id.
- `role`: String, one of user | model - One of: user, model.
- `text`: String - The message's text.
- `target_url`: String - Admin page the message links to.
- `action`: String, one of RESPONSE | REDIRECT | OBJECT_RESPONSE | ERROR | CLARIFY | PROPOSED_ACTION - One of: RESPONSE, REDIRECT, OBJECT_RESPONSE, ERROR, CLARIFY, PROPOSED_ACTION.
- `options`: String[] - Quick replies offered under the message.
- `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[] - Pictures attached to the message.
  - `media`: ObjectId, → Media
  - `gcs_url`: String
  - `gemini_uri`: String
  - `gemini_interaction_id`: String
  - `_id`: ObjectId
- `products`: ObjectId[], → Product - Product ids.
- `services`: ObjectId[], → Service - Service ids.
- `blogposts`: ObjectId[], → BlogPost - BlogPost ids.
- `programs`: ObjectId[], → Program - Program ids.
- `suggestions`: Object[] - Next steps suggested under the message.
  - `text`: String
  - `route`: String
  - `item_id`: ObjectId
  - `medias`: Mixed[]
  - `picture`: ObjectId
  - `_id`: ObjectId
- `is_system`: Boolean, default false - Written by the platform, not by the owner or the AI.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### Email

- `title`: String - The email's heading.
- `text1`: String - Main text of the email; what each text slot means depends on type.
- `text2`: String - Second text slot of the email.
- `text3`: String - Third text slot of the email.
- `text4`: String - Fourth text slot of the email.
- `text5`: String - Fifth text slot of the email.
- `text6`: String - Sixth text slot of the email.
- `text7`: String - Seventh text slot of the email.
- `text8`: String - Eighth text slot of the email.
- `text9`: String - Ninth text slot of the email.
- `text10`: String - Tenth text slot of the email.
- `type`: String, required - Which email this is (welcome, confirmation, resetPassword, newsletter...).
- `google_calendar`: String, default "Add to Google Calendar" - Label of the add-to-Google-Calendar link.
- `outlook_calendar`: String, default "Add to Outlook Calendar" - Label of the add-to-Outlook link.
- `apple_calendar`: String, default "Add to Apple Calendar" - Label of the add-to-Apple-Calendar link.
- `product_text`: String - Text for an order of products.
- `program_text`: String - Text for a program purchase.
- `priceplan_text`: String - Text for a subscription.
- `template_id`: ObjectId, → EmailTemplate - EmailTemplate.id.
- `show_phone`: Boolean, default false - Show the business's phone number in the email.
- `product_text_fulfilled`: String - Text when the order was fulfilled.
- `booking_text`: String - Text for a booking.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### EmailTemplate

The design and text of one of the emails the site sends.

- `name`: String - Name of the template.
- `rows`: Object[] - The template's layout, row by row, as the editor keeps it.
  - `columns`: Object[]
    - `content`: Object[]
      - `type`: String, required, one of text | image | logo | link | email | firstname | lastname | name | address | phone_number | city | state | country | imgSrc | icon | custom
      - `width`: Number, required, default 100
      - `fallbackText`: String
      - `fontSize`: Number
      - `fontWeight`: String, one of 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
      - `fontColor`: String, default "#000000"
      - `fontFamily`: String, default "Arial,Helvetica Neue,Helvetica,sans-serif"
      - `fontStyle`: String, one of normal | italic | oblique
      - `textAlign`: String, one of left | right | center
      - `display`: String, one of block | inline, default "block"
      - `icon`: String
      - `image`: String
      - `backgroundColor`: String
      - `innerHTML`: String
      - `link`: String
      - `_id`: ObjectId
    - `width`: Number, required, default 100
    - `justifyContent`: String, one of start | end | center | space-between | space-around | space-evenly
    - `paddingLeft`: Number, default 0
    - `paddingRight`: Number, default 0
    - `paddingTop`: Number, default 0
    - `paddingBottom`: Number, default 0
    - `borderLeft.width`: Number, default 0
    - `borderLeft.color`: String, default "#000000"
    - `borderRight.width`: Number, default 0
    - `borderRight.color`: String, default "#000000"
    - `borderTop.width`: Number, default 0
    - `borderTop.color`: String, default "#000000"
    - `borderBottom.width`: Number, default 0
    - `borderBottom.color`: String, default "#000000"
    - `backgroundColor`: String
    - `_id`: ObjectId
  - `_id`: ObjectId
- `previewHTML`: String, required - HTML for the editor's preview.
- `compiledHTML`: String, required - HTML sent in emails, with placeholders.
- `fallbackValues`: Mixed - Values used for placeholders that have none.
- `owner_id`: ObjectId, required - User.id of who made it.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date
