# Builderly API reference

Base URL: http://bulgariandebateassociation.max-media.io
Generated from the running server on 2026-11-09T16:55:32.704Z. 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

### Coupons & vouchers

Coupons are discount codes customers enter at checkout - a fixed amount, a percentage, free shipping or buy X get Y - limited to products, collections, events or a minimum subtotal. Vouchers are app-exclusive offers.

#### Coupon

##### Retrieve the coupon capture
`GET /coupon_capture`
Lightweight endpoint for the ionic app to trigger the couponCapture middleware. The middleware runs before this handler, validates ?coupon= and sets the pending_coupon cookie.
- Access: Public
- Status codes: 200

##### Retrieve the coupon code
`GET /coupon_code`
The coupon with a code (coupon_code). Reads the code from the request body even though it is a GET. A valid coupon counts one use; 404 when the code is unknown, outside its dates or used up.
- Access: Optional user
- Body:
  - `coupon_code` (string): The coupon code's coupon code.
- Status codes: 200, 404, 500
- Models: Coupon

##### List all coupon codes
`GET /coupon_code/:code`
Returns if a coupon is valid. 404 when the code is unknown, outside its dates or used up. A valid one counts a use.
- Access: Optional user
- Path:
  - `code` (string): The coupon code's code.
- Status codes: 200, 404, 500
- Models: Coupon

##### List all coupon infos
`GET /coupon_info/:code`
Returns public coupon details by code without incrementing uses. 404 when the code is unknown. Does not count a use.
- Access: Public
- Path:
  - `code` (string): The coupon info's code.
- Status codes: 200, 404, 500
- Models: Coupon

##### List all coupons
`GET /coupons`
The coupons. Reads no token, so every coupon code can be listed by anyone.
- Access: Public
- Returns: a list of Coupon objects.
- Status codes: 200, 500
- Models: Coupon

##### Create a coupon
`POST /coupons`
Creates a coupon. Answers 201 with the coupon.
- Access: Admin token
- Returns: the Coupon object.
- Status codes: 201, 400, 401, 500
- Models: Coupon

##### Retrieve a coupon
`GET /coupons/:id`
One coupon. 404 when there is no such coupon.
- Access: Public
- Path:
  - `id` (string): The ID of the coupon.
- Returns: the Coupon object.
- Status codes: 200, 404, 500
- Models: Coupon

##### Update a coupon
`PATCH /coupons/:id`
Updates a coupon. Fields outside the allowed list are ignored. Answers the coupon, or 404.
- Access: Admin token
- Path:
  - `id` (string): The ID of the coupon.
- Returns: the Coupon object.
- Status codes: 200, 400, 401, 404, 500
- Models: Coupon

##### Delete a coupon
`DELETE /coupons/:id`
Deletes a coupon. Answers the deleted coupon, or 404.
- Access: Admin token
- Path:
  - `id` (string): The ID of the coupon.
- Returns: an empty answer on success.
- Status codes: 200, 401, 404, 500
- Models: Coupon

#### Voucher

##### List all vouchers
`GET /vouchers`
The vouchers. Answers an array of every record.
- Access: Public
- Query:
  - `authenticated` (string): Only vouchers with this authenticated.
- Returns: a list of Voucher objects.
- Status codes: 200, 500
- Models: Voucher

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

## Objects

### Coupon

A discount code for checkout.

- `code`: String, required - Coupon code.
- `name`: String, required - Coupon name (not shown to end user).
- `fixed_discount`: Number - Fixed amount of discount.
- `percentage_discount`: Number - Discount in percent, e.g. 90 for 90% off.
- `shipping_discount`: Number - Shipping discount discount.
- `sale_discount`: Number - Sale discount discount.
- `quantity_discount.buy`: Number
- `quantity_discount.get`: Number
- `apply_to_all_orders`: Boolean - The coupon works on any order.
- `apply_to_all_products`: Boolean - The coupon works on every product.
- `apply_to_product`: String - The NAME of the one product the coupon works on, exactly as the product is named (not its id): the cart matches it against product names.
- `apply_to_event`: String - The NAME of the one event the coupon works on, exactly as the event is named.
- `apply_to_all_events`: Boolean - The coupon works on every event.
- `apply_to_collection`: String - The full NAME of the one category the coupon works on ("product/Candles"), not its id: the cart matches it against category names.
- `apply_to_minimum_subtotal`: Number - Minimum total amount.
- `start_date`: Date - When the coupon starts working; now when left out.
- `end_date`: Date - When it stops working, unless no_end_date is set.
- `no_end_date`: Boolean, default false - The coupon never expires; end_date is ignored.
- `limit_uses`: Number - How many times the coupon can be used in all; null (the default) for unlimited.
- `uses`: Number, default 0 - How many times it has been used (do not set).
- `_id`: ObjectId

### Voucher

- `title`: String - Title of voucher.
- `body`: String - Body of voucher.
- `media`: ObjectId - Attached media object with id.
- `show_to`: String, default "0" - Attached media object with id.
- `coupon`: ObjectId, → Coupon - Attached Coupon id.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date
