# 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

### Events

Events are ticketed events: dates, ticket tiers with prices, a venue and, optionally, a seating plan with seats to choose from. Venues and seating plans are set up once and reused across events.

#### Event

##### List all events
`GET /events`
The events page, or the events as JSON with ?ionic. Each event with its venue, dates and pictures only.
- Access: Optional user
- Returns: a list of Event objects.
- Status codes: 200, 500
- Models: Event

##### Create an event
`POST /events`
Creates an event. Answers 201 with the event.
- Access: User token
- Returns: the Event object.
- Status codes: 201, 400, 401, 500
- Models: Event

##### Retrieve an event
`GET /events/:id`
An event's page, or JSON with ?ionic. With its venue. 404 when there is no such event.
- Access: Optional user
- Path:
  - `id` (string): The ID of the event.
- Returns: the Event object.
- Status codes: 200, 404, 500
- Models: Event

##### Update an event
`PATCH /events/:id`
Updates an event. Answers the event, or 404.
- Access: User token
- Path:
  - `id` (string): The ID of the event.
- Returns: the Event object.
- Status codes: 200, 400, 401, 404, 500
- Models: Event

##### Delete an event
`DELETE /events/:id`
Deletes an event. Answers the deleted event. Its tickets stay.
- Access: User token
- Path:
  - `id` (string): The ID of the event.
- Returns: an empty answer on success.
- Status codes: 200, 401, 404, 500
- Models: Event

#### Seating Plan

##### List all seating plans
`GET /seating-plans`
The seating plans. Answers an array of every record.
- Access: Public
- Returns: a list of SeatingPlan objects.
- Status codes: 200, 500
- Models: SeatingPlan

##### Create a seating plan
`POST /seating-plans`
Creates a seating plan. Answers 201 with the created record; 400 with the reason when a field is invalid.
- Access: User token
- Returns: the SeatingPlan object.
- Status codes: 201, 400, 401, 500
- Models: SeatingPlan

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

##### Update a seating plan
`PATCH /seating-plans/:id`
Updates a seating plan. Fields outside the allowed list are silently ignored. Answers the updated record, or 404.
- Access: User token
- Path:
  - `id` (string): The ID of the seating plan.
- Returns: the SeatingPlan object.
- Status codes: 200, 400, 401, 404, 500
- Models: SeatingPlan

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

#### Ticket

##### Retrieve an events date
`GET /events-date/:id/:date_index`
An event date's page with its seating plan and ticket tiers. date_index is the date's position in the event's dates. 404 when there is no such event or date.
- Access: Optional user
- Path:
  - `id` (string): The ID of the events date.
  - `date_index` (string): The events date's date index.
- Status codes: 200, 404, 500
- Models: Event

##### Retrieve a ticket
`GET /ticket/:id`
A ticket's page with its QR code. 404 when there is no such ticket. JSON with ?ionic.
- Access: Optional user
- Path:
  - `id` (string): The ID of the ticket.
- Returns: the Ticket object.
- Status codes: 200, 404, 500
- Models: Ticket, Event, Order, QrCode

##### Create a ticket
`POST /tickets`
Holds, releases or sells seats for the visitor (tickets, freeze). Seats are held for the visitor until expiration_time, then released if not paid. 400 for an invalid section, date or seat, a seat already taken or a full section.
- Access: Optional user
- Body:
  - `findIndex` (string): The ticket's find index.
  - `splice` (string): The ticket's splice.
  - `push` (string): The ticket's push.
  - `tickets` (string): The ticket's tickets.
  - `freeze_action` (string): The ticket's freeze action.
  - `bought_by_admin` (string): The ticket's bought by admin.
  - `freeze` (string): The ticket's freeze.
- Returns: the Ticket object.
- Status codes: 201, 400, 500
- Models: Event, Ticket

#### Venue

##### List all venues
`GET /venues`
The venues. Answers an array of every venue. Reads no token.
- Access: Public
- Returns: a list of Venue objects.
- Status codes: 200, 500
- Models: Venue

##### Create a venue
`POST /venues`
Creates a venue. Answers 201 with the venue.
- Access: User token
- Returns: the Venue object.
- Status codes: 201, 400, 401, 500
- Models: Venue

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

##### Update a venue
`PATCH /venues/:id`
Updates a venue. Answers the venue, or 404.
- Access: User token
- Path:
  - `id` (string): The ID of the venue.
- Returns: the Venue object.
- Status codes: 200, 400, 401, 404, 500
- Models: Venue

##### Delete a venue
`DELETE /venues/:id`
Deletes a venue. Answers the deleted venue. Its seating plans and events keep pointing at it.
- Access: User token
- Path:
  - `id` (string): The ID of the venue.
- Returns: an empty answer on success.
- Status codes: 200, 401, 404, 500
- Models: Venue

#### AI builder

##### Create a seating plan with AI
`POST /create-new-seating-plan`
AI: designs a seating plan for one of the venues. 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 seating plan, 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 seating plan to change.
- Status codes: 200, 401, 500

##### Generate a seating plan with AI
`POST /generate-seating-plan`
AI: routes a request for a seating plan to create-new-seating-plan. 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 seating plan, 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 seating plan with AI
`POST /modify-seating-plan`
AI: routes a change to a seating plan. 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 seating plan, in plain words - any language.
  - `item_data` (string): The current seating plan, 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

## Objects

### Event

A ticketed event at a venue.

- `name`: String, required - Name of the event.
- `venue`: ObjectId, required, → Venue - Venue.id.
- `description`: String, required - About the event.
- `medias`: Object[] - The event's pictures.
  - `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
- `dates`: Object[] - When it takes place, one entry per date.
  - `date`: Date, required
  - `seating_plan.id`: ObjectId, required, → SeatingPlan
  - `seating_plan.sections`: Object[], required
    - `subsections`: Object[], required
      - `rows`: Object[], required
      - `edit`: Boolean, required
      - `widest_column`: Number, required
      - `justify`: String, required
      - `left`: Number
      - `top`: Number
    - `section_type`: String, required
    - `name`: String, required
    - `top`: Number
    - `left`: Number
    - `color`: String
    - `premium_color`: String
    - `rotation`: Number
    - `tier`: Number, default 0
    - `max_seats`: Number
    - `tickets`: ObjectId[], → Ticket
  - `_id`: ObjectId
- `tier_1.name`: String
- `tier_1.price`: Number
- `tier_2.name`: String
- `tier_2.price`: Number
- `tier_3.name`: String
- `tier_3.price`: Number
- `tier_4.name`: String
- `tier_4.price`: Number
- `tier_5.name`: String
- `tier_5.price`: Number
- `tier_6.name`: String
- `tier_6.price`: Number
- `tier_7.name`: String
- `tier_7.price`: Number
- `tier_8.name`: String
- `tier_8.price`: Number
- `tier_9.name`: String
- `tier_9.price`: Number
- `tier_10.name`: String
- `tier_10.price`: Number
- `tier_11.name`: String
- `tier_11.price`: Number
- `tier_12.name`: String
- `tier_12.price`: Number
- `tier_13.name`: String
- `tier_13.price`: Number
- `tier_14.name`: String
- `tier_14.price`: Number
- `tier_15.name`: String
- `tier_15.price`: Number
- `owner_id`: ObjectId, required, → User - User.id.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### Order

A completed purchase.

- `organization_name`: String - Customer's company name.
- `firstname`: String, required - Customer's first name.
- `lastname`: String, required - Customer's last name.
- `email`: String, required - Customer's email.
- `phone_number`: String, required - Customer's phone number.
- `address`: String, required - Delivery address.
- `city`: String, required - Delivery city.
- `state`: String - Delivery state or region.
- `country`: String, required - Delivery country.
- `zip_code`: String, required - Delivery postal code.
- `tax_identification_number`: String - Customer's tax number, for an invoice.
- `notes`: String - What the customer wrote with the order.
- `products`: String[] - Product ids ordered.
- `products_obj`: Object[] - The products ordered, as they were then: options, quantity and price.
  - `product_id`: ObjectId, → Product
  - `name`: String
  - `options`: String
  - `price`: Number
  - `quantity`: Number
  - `_id`: ObjectId
- `programs`: String[] - Program ids ordered.
- `programs_obj`: Object[] - The programs ordered, as they were then.
  - `program_id`: ObjectId, → Program
  - `title`: String
  - `price`: Number
  - `_id`: ObjectId
- `tickets`: ObjectId[], → Ticket - Ticket ids.
- `donations`: ObjectId[], → Donation - Donation ids.
- `donations_obj`: Object[] - The donations made with the order.
  - `donation_id`: ObjectId, → Donation
  - `name`: String
  - `options`: String
  - `price`: Number
  - `quantity`: Number
  - `_id`: ObjectId
- `value`: Number - Total of the order.
- `finalized`: Boolean, default false - Checkout was completed.
- `fulfilled`: Boolean, default false - The order was sent or handed over.
- `paid`: Boolean, default false - The order is paid.
- `delivery_method`: String - How it is delivered (courier, office, pickup...).
- `payment_method`: String - How it is paid (card, cash, invoice, PayPal).
- `shipping_cost`: Number - What delivery costs.
- `coupon_code`: String - The coupon used.
- `owner_id`: ObjectId, → User - User.id.
- `reference_id`: String - The order's id at the payment provider.
- `rating_mail_sent`: Boolean, default false - The email asking for a rating was sent.
- `shipping_label`: String - The courier's shipment label.
- `shipping_option`: String, one of address | office, default "address" - One of: address, office.
- `shipping_message`: String - The courier's last status message.
- `shipping_office`: String - The courier office it is delivered to.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### QrCode

- `ticket`: ObjectId, required, → Ticket - Ticket.id.
- `status`: String, one of used | not-used, default "not-used" - One of: used, not-used.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### SeatingPlan

The seats of a venue, which event tickets can be sold for.

- `name`: String, required - Name of the seating plan.
- `sections`: Object[], required - The hall's sections, each with its rows and seats.
  - `subsections`: Object[], required
    - `rows`: Object[], required
      - `columns`: Object[], required
      - `top`: Number
      - `left`: Number
      - `formated_row_index`: Number, required
    - `edit`: Boolean, required
    - `widest_column`: Number, required
    - `justify`: String, required
    - `left`: Number
    - `top`: Number
  - `section_type`: String, required
  - `name`: String, required
  - `top`: Number
  - `left`: Number
  - `color`: String
  - `premium_color`: String
  - `rotation`: Number
  - `max_seats`: Number
- `venue`: ObjectId, required, → Venue - Venue.id.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### Ticket

- `event`: ObjectId, required, → Event - Event.id.
- `date`: Number, required - Index of the event date.
- `section`: Number - Index of the section.
- `subsection`: Number - Index of the subsection.
- `row`: Number - Index of the row.
- `seat`: Number - Index of the seat.
- `row_index_formated`: Number - Row number as shown to the buyer.
- `seat_index_formated`: Number - Seat number as shown to the buyer.
- `lead_id`: ObjectId, → Lead - Lead.id.
- `socket_id`: String - The buyer's live connection, while the seat is held.
- `tier.name`: String
- `tier.price`: Number
- `status`: String, one of frozen | waiting-for-payment | paid | frozen-by-admin | bought-by-admin, default "frozen" - One of: frozen, waiting-for-payment, paid, frozen-by-admin, bought-by-admin.
- `expiration_time`: Date, required - When a held seat is released if not paid.
- `qr_code`: ObjectId, → QrCode - QrCode.id.
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date

### Venue

A place where events happen, with its address.

- `name`: String, required - Name of the venue.
- `origin_address`: ObjectId, required, → OriginAddress - OriginAddress.id.
- `address`: String, required - The venue's address.
- `medias`: Object[] - Pictures of the venue.
  - `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
- `_id`: ObjectId
- `updatedAt`: Date
- `createdAt`: Date
