Introduction
The Moj Web API provides programmatic access to manage your businesses, websites, media, calendar, menu items, and inquiries. It is designed for:
- Automation - Sync availability calendars, update menus automatically
- Integrations - Connect with booking platforms, POS systems, CRMs
- AI Agents - Enable AI assistants to manage your web presence
The base URL for all API requests is:
https://moj-web.comAll endpoints documented below are under /api/v1, accept and return JSON unless noted otherwise, and only expose resources accessible to the API key owner.
Authentication
All API requests require authentication using a Bearer token. You can create and manage API keys from your dashboard.
Getting an API Key
- Log in to your dashboard
- Open Settings > API Keys
- Click "Create API Key"
- Select the scopes (permissions) you need
- Copy and securely store the generated key
Using Your API Key
Include the API key in the Authorization header of every request:
curl -H "Authorization: Bearer mw_live_your_api_key_here" \
https://moj-web.com/api/v1/businessesThe scheme must be exactly Bearer. Missing, malformed, invalid, or expired keys return 401; a valid key without the required scope returns 403.
Key Prefixes
| Prefix | Environment | Description |
|---|---|---|
mw_live_ | Live | Prefix used by keys created in the dashboard |
mw_test_ | Test | Accepted format for test keys when issued |
The prefix identifies the key format; it does not select a separate API base URL or sandbox. Treat every key as a secret. The full value is shown only when the key is created.
Scopes
API keys have scopes that define what operations they can perform. Request only the scopes your application needs for better security.
| Scope | Description |
|---|---|
business:read | Read business information |
business:write | Create and update businesses |
business:delete | Delete businesses |
website:read | Read website content |
website:write | Update website content and theme |
website:publish | Publish and unpublish websites |
media:read | List media files |
media:write | Upload media files |
media:delete | Delete media files |
calendar:read | Read availability calendar |
calendar:write | Manage blocked dates |
menu:read | Read menu items |
menu:write | Create and update menu items |
menu:delete | Delete menu items |
inquiry:read | Read inquiries |
inquiry:write | Create inquiries and update inquiry status |
The dashboard issues keys with one or more of the listed scopes.
Endpoints Reference
Businesses
/api/v1/businessesList accessible businesses
Query parameters
- type — optional: APARTMENT, VILLA, RESTAURANT, CAFE, or ACTIVITY_VENUE
- limit — default 50, maximum 100
- offset — default 0
Success
- 200 — { businesses, pagination: { total, limit, offset, hasMore } }
/api/v1/businessesCreate a business
Request body
- Required: type (APARTMENT | VILLA | RESTAURANT | CAFE | ACTIVITY_VENUE), name (at least 2 characters)
- Optional: description, address, city, country (default: Hrvatska), phone, email, amenities (string[])
Success
- 201 — { business }
Endpoint-specific errors
- 400 — invalid body
/api/v1/businesses/:idGet business details and resource counts
Success
- 200 — { business }
Endpoint-specific errors
- 404 — business not found or not accessible
/api/v1/businesses/:idUpdate a business
Request body
- Any POST business field; only supplied fields are updated
Success
- 200 — { business }
Endpoint-specific errors
- 400 — invalid body
- 404 — business not found or not accessible
/api/v1/businesses/:idDelete a business and its stored files
Success
- 200 — { deleted: true }
Endpoint-specific errors
- 404 — business not found or not accessible
Websites
/api/v1/websitesList accessible websites
Query parameters
- isPublished — optional: true or false
- page — default 1, minimum 1
- limit — default 50, range 1–100
Success
- 200 — { websites, pagination: { page, limit, totalCount, totalPages } }
/api/v1/websites/:idGet website content, theme, and business details
Success
- 200 — { website }
Endpoint-specific errors
- 404 — website not found or not accessible
/api/v1/websites/:idUpdate content or theme
Request body
- At least one of: content (JSON object), themeId (string), themeConfig (JSON object)
- String values inside content are sanitized before storage
Success
- 200 — { website }
Endpoint-specific errors
- 400 — invalid JSON, invalid fields, or no fields to update
- 404 — website not found or not accessible
/api/v1/websites/:id/publishPublish a website
Success
- 200 — { website: { id, subdomain, isPublished, publishedAt } }
Endpoint-specific errors
- 400 — website is already published
- 404 — website not found or not accessible
/api/v1/websites/:id/publishUnpublish a website
Success
- 200 — { website: { id, subdomain, isPublished, publishedAt } }
Endpoint-specific errors
- 400 — website is not published
- 404 — website not found or not accessible
Media
/api/v1/mediaList media using cursor pagination
Query parameters
- businessId — optional accessible business filter
- cursor — optional media ID returned as pagination.nextCursor
- limit — default 50, maximum 100
Success
- 200 — { media, pagination: { hasMore, nextCursor } }
Endpoint-specific errors
- 404 — business filter is not accessible
/api/v1/mediaUpload and process an image
Request body
- multipart/form-data: file (required) and businessId (required)
- Accepted input: JPG, PNG, or WebP; maximum 10 MB. Stored output is WebP.
Success
- 201 — { media }
Endpoint-specific errors
- 400 — missing, unsupported, oversized, or unprocessable file
- 404 — business not found or not accessible
/api/v1/media/:idGet media details
Success
- 200 — { media }
Endpoint-specific errors
- 404 — media not found or not accessible
/api/v1/media/:idDelete unused media
Success
- 200 — { deleted: true }
Endpoint-specific errors
- 404 — media not found or not accessible
- 409 — media is still referenced; response includes usage details
Calendar
/api/v1/calendarList calendar blocks overlapping a date range
Query parameters
- One of businessId or unitId is required; businessId returns business-wide blocks only
- unitId returns blocks for that unit plus business-wide blocks
- startDate and endDate — optional YYYY-MM-DD boundaries; endDate is exclusive
Success
- 200 — { blockedDates }
Endpoint-specific errors
- 400 — missing target, invalid date, or invalid range
- 404 — business or unit not found
/api/v1/calendarCreate a calendar block
Request body
- Required: businessId or unitId, startDate, endDate (YYYY-MM-DD; endDate is exclusive)
- Optional: reason, guestName, note (string or null)
Success
- 201 — { blockedDate }
Endpoint-specific errors
- 400 — invalid body or date range
- 404 — business or unit not found
- 409 — range overlaps a calendar block or reservation
/api/v1/calendar/:idGet a calendar block
Success
- 200 — { blockedDate }
Endpoint-specific errors
- 404 — blocked date not found or not accessible
/api/v1/calendar/:idUpdate a calendar block
Request body
- Any of startDate, endDate (YYYY-MM-DD), reason, guestName, or note; the target business/unit cannot be changed
Success
- 200 — { blockedDate }
Endpoint-specific errors
- 400 — invalid JSON or date range
- 404 — blocked date not found or not accessible
- 409 — range overlaps a calendar block or reservation
/api/v1/calendar/:idDelete a calendar block
Success
- 200 — { success: true }
Endpoint-specific errors
- 404 — blocked date not found or not accessible
Inquiries
/api/v1/inquiriesList accessible inquiries
Query parameters
- websiteId — optional accessible website filter
- status — optional: NEW, CONTACTED, ANSWERED, or ARCHIVED
- limit — default 50, maximum 100
- offset — default 0
Success
- 200 — { inquiries, pagination: { total, limit, offset, hasMore } }
/api/v1/inquiriesCreate an inquiry with optional metadata
Request body
- Required: websiteId, name, email, message
- Optional: phone, checkIn, checkOut (ISO 8601 strings), guests (positive integer), metadata (JSON object)
Success
- 201 — { inquiry }
Endpoint-specific errors
- 400 — invalid JSON or fields
- 404 — website not found or not accessible
/api/v1/inquiries/:idGet inquiry details
Success
- 200 — { inquiry }
Endpoint-specific errors
- 404 — inquiry not found or not accessible
/api/v1/inquiries/:idUpdate inquiry workflow status
Request body
- Required: status — NEW, CONTACTED, ANSWERED, or ARCHIVED
Success
- 200 — { inquiry }
Endpoint-specific errors
- 400 — invalid JSON or status
- 404 — inquiry not found or not accessible
Example Requests
List your businesses
curl -X GET "https://moj-web.com/api/v1/businesses" \
-H "Authorization: Bearer mw_live_your_api_key"Response:
{
"businesses": [
{
"id": "clx1234567890",
"type": "APARTMENT",
"name": "Apartments Sunset",
"description": "Beautiful seaside apartments",
"address": "Obala 123",
"city": "Split",
"country": "Croatia",
"phone": "+385 21 123 456",
"website": {
"id": "clx0987654321",
"subdomain": "apartments-sunset",
"isPublished": true
}
}
],
"pagination": {
"total": 1,
"limit": 50,
"offset": 0,
"hasMore": false
}
}Create a business
curl -X POST "https://moj-web.com/api/v1/businesses" \
-H "Authorization: Bearer mw_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"type": "RESTAURANT",
"name": "Konoba Mare",
"description": "Traditional Dalmatian cuisine",
"address": "Riva 45",
"city": "Dubrovnik",
"country": "Croatia",
"phone": "+385 20 321 654"
}'List calendar blocks
curl -X GET "https://moj-web.com/api/v1/calendar?unitId=clx_unit_id&startDate=2026-07-01&endDate=2026-09-01" \
-H "Authorization: Bearer mw_live_your_api_key"Block dates in calendar
curl -X POST "https://moj-web.com/api/v1/calendar" \
-H "Authorization: Bearer mw_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"unitId": "clx_unit_id",
"startDate": "2026-07-15",
"endDate": "2026-07-22",
"reason": "BOOKING",
"guestName": "John Smith"
}'Upload media
curl -X POST "https://moj-web.com/api/v1/media" \
-H "Authorization: Bearer mw_live_your_api_key" \
-F "businessId=clx1234567890" \
-F "file=@/path/to/image.jpg"Add menu item
curl -X POST "https://moj-web.com/api/v1/menu" \
-H "Authorization: Bearer mw_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"businessId": "clx1234567890",
"name": "Grilled Sea Bass",
"description": "Fresh daily catch with seasonal vegetables",
"price": 25.00,
"category": "Main Courses",
"isAvailable": true
}'Create inquiry with metadata
curl -X POST "https://moj-web.com/api/v1/inquiries" \
-H "Authorization: Bearer mw_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"websiteId": "clx_website_id",
"name": "Vila Sunset",
"email": "[email protected]",
"phone": "+385 91 123 4567",
"message": "Lead from Booking.com scraper",
"metadata": {
"source": "booking-scraper",
"booking_url": "https://booking.com/hotel/...",
"rating": 9.2,
"lead_score": 85
}
}'Update inquiry status
Supported statuses: NEW, CONTACTED, ANSWERED, ARCHIVED.
curl -X PATCH "https://moj-web.com/api/v1/inquiries/clx_inquiry_id" \
-H "Authorization: Bearer mw_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"status": "ANSWERED"}'Publish a website
curl -X POST "https://moj-web.com/api/v1/websites/clx_website_id/publish" \
-H "Authorization: Bearer mw_live_your_api_key"List only published websites
curl -X GET "https://moj-web.com/api/v1/websites?isPublished=true" \
-H "Authorization: Bearer mw_live_your_api_key"Error Handling
The API uses standard HTTP status codes to indicate success or failure:
| Status Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created - Resource created successfully |
| 400 | Bad Request - Invalid parameters or request body |
| 401 | Unauthorized - Missing or invalid API key |
| 403 | Forbidden - Insufficient scope permissions |
| 404 | Not Found - Resource does not exist |
| 409 | Conflict - Date overlap or media is still in use |
| 429 | Too Many Requests - API key rate limit exceeded |
| 500 | Internal Server Error - Something went wrong |
Error responses include a JSON body with an error field:
{
"error": "Missing Authorization header"
}{
"error": "Insufficient permissions. Required: business:write"
}{
"error": "Business not found"
}Rate Limiting
API requests are rate limited to ensure fair usage and maintain service quality. Rate limits are configurable per API key.
Newly created keys default to 1000 requests per 60-second window. The limit is enforced independently for each API key and may be configured differently for an individual key.
When the limit is exceeded, the API returns 429 Too Many Requests with this body:
{
"error": "Rate limit exceeded"
}Rate-limit counters or reset timestamps are not currently included in response headers. Retry later; the API does not currently report the exact reset time.
Need help? Contact support or check our guides in the dashboard.