Recommended URLs for agents
| URL | Format | Best for |
|---|---|---|
/APIDocs/agents.html |
text/markdown (live) | Primary — always current version from codebase |
/APIDocs/agents.md |
Markdown file | Static export, curl/wget, offline tools |
/APIDocs/llms.txt |
Plain text index | Discovery — lists all agent-readable resources |
/APIDocs/openapi/rpm-pro-api.openapi.yaml |
OpenAPI 3.0 | Code generation, Postman import |
HTTP headers on
agents.html:
X-RPM-Docs-Version (5.9.3.3),
X-RPM-Docs-Format: agents-markdown,
Content-Type: text/markdown
Full reference (preview)
Below is the same content served by agents.html. Copy or fetch the raw URL for parsing.
# RPM-PRO REST API — Agent Reference
> **For AI agents:** This is the canonical machine-readable API documentation for RPM-PRO.
> Prefer this file over scraping HTML pages. Version **5.9.3.3**.
## Quick links
| Resource | URL |
|----------|-----|
| **This document (raw markdown)** | `/APIDocs/agents.md` or `/APIDocs/agents.html` |
| OpenAPI 3.0 spec | `/APIDocs/openapi/rpm-pro-api.openapi.yaml` |
| Postman collection | `/APIDocs/postman/RPM-PRO-API.postman_collection.json` |
| Human HTML docs | `/APIDocs/index.html` |
| Agent landing page | `/APIDocs/for-agents.html` |
## Architecture
- **Framework:** WiseLoop `wlRestService`
- **Base URL pattern:** `/API/{controller}/V1/{action}.json`
- **Default format:** JSON
- **Action mapping:** `settingsGet()` → GET, `quotePost()` → POST
- **Services:** PSR-4 classes in `src/RPM/`, accessed via `rpm_*_service()` factories
- **Rate limit:** 25 requests per 5 seconds per IP
- **Response cache:** 10 minutes in `API/cache/`
## Authentication
| Level | Mechanism | Notes |
|-------|-----------|-------|
| public | None | Most endpoints |
| session | PHP session cookie (`tenantId`) | `publicAuth/me`, `leaseApplications/apply` |
| admin | `adminAPIKey` + `adminAPISecret` via `/API/auth/V1/login.json` | Auth only — no protected data endpoints yet |
**Admin API note:** All current business endpoints are on the public allowlist (`MySqlAuthHandler.html`). Admin login establishes `wlSession` but does not gate data access today. See Admin API section below.
## Service registry (factories)
| Factory | Service class |
|---------|---------------|
| `rpm_site_settings_service()` | `RPM\Site\SiteSettingsService` |
| `rpm_site_lead_service()` | `RPM\Site\SiteLeadService` |
| `rpm_site_blog_service()` | `RPM\Site\BlogService` |
| `rpm_property_catalog_service()` | `RPM\Property\CatalogService` |
| `rpm_booking_quote_calculator()` | `RPM\Booking\Quote\QuoteCalculator` |
| `rpm_booking_reservation_service()` | `RPM\Booking\Reservation\ReservationService` |
| `rpm_finance_invoice_service()` | `RPM\Finance\InvoiceService` |
| `rpm_encryption_service()` | `RPM\Security\EncryptionService` |
## Endpoints (ordered for review)
Order: Authentication → Properties → Booking → Website/CMS → Blog & Attractions → Integrations
### Admin Authentication
Controller: auth · Establishes wlSession for admin* endpoints. Login is public; user requires session.
#### `POST` /API/auth/V1/login.json
**Auth:** public
Admin API key login; sets wlSession.
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `adminAPIKey` | string | Yes | From admins table |
| `adminAPISecret` | string | Yes | API secret |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `adminId` | int | No | Admin ID |
| `adminFirstName` | string | No | First name |
| `adminLastName` | string | No | Last name |
| `adminRole` | string | No | 0=super, 1=landlord, 2=svc_rep, 3=HK |
| `superuser` | string | No | Superuser flag |
**Example request:**
```json
{ "adminAPIKey": "your-key", "adminAPISecret": "your-secret" }
```
**HTTP errors:** 401 Invalid credentials
#### `GET` /API/auth/V1/user.json
**Auth:** admin
Current admin session.
**HTTP errors:** 401 Unauthorized
### Admin Properties
Controller: adminProperties · Service: CatalogService::adminListProperties / adminGetPropertyById
#### `GET` /API/adminProperties/V1/properties.json
**Auth:** admin
List all properties (LTR+VR by default). Includes archived/off-website unless filtered.
**Service:** `rpm_property_catalog_service()`
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string | No | all (default), ltr, or vr catalog mode |
| `page` | int | No | Page number (default 1) |
| `limit` | int | No | Per page (default 20, max 100) |
| `includeArchived` | string | No | 1 = include archived properties |
| `onWebsite` | string | No | 0 or 1 to filter onWebsite flag |
| `search` | string | No | Name/address search (max 128 chars) |
| `beds` | string | No | Bedroom filter |
| `baths` | string | No | Bathroom filter |
| `location` | string | No | Address search |
| `propertyType` | string | No | propertyType filter (when in list mode) |
| `pets` | string | No | 1 = pets allowed only |
| `featured` | string | No | 1 = featured VR listings only |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Property objects with isArchived |
| `page` | int | No | Current page |
| `limit` | int | No | Page size |
| `total` | int | No | Total matching properties |
| `ltrTotal` | int | No | LTR count when type=all |
| `vrTotal` | int | No | VR count when type=all |
**HTTP errors:** 401 Unauthorized, 403 Forbidden (wrong role)
#### `GET` /API/adminProperties/V1/properties/{id}.json
**Auth:** admin
Single property by ID (LTR or VR).
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | propertyId |
**HTTP errors:** 404 Property not found
#### `GET` /API/adminProperties/V1/property/{id}.json
**Auth:** admin
Alias for single property by ID.
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | propertyId |
**HTTP errors:** 404 Property not found
#### `POST` /API/adminProperties/V1/properties.json
**Auth:** admin
Create LTR or VR property (type field).
**Service:** `CatalogService::adminCreateProperty()`
**Roles:** `super_admin`
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `type` | string | Yes | ltr or vr |
| `propertyName` | string | Yes | Unique property name (max 128) |
| `propertyDesc` | string | Yes | Description (max 5000) |
| `propertyRate` | string | Yes | Nightly/monthly rate |
| `latePenalty` | string | No | Required for LTR; default 0.00 for VR |
| `propertyDeposit` | string | No | Required for LTR |
| `petsAllowed` | int | No | 0 or 1 |
| `landlord` | int | No | Landlord adminId (default: caller) |
| `propertyAddress` | string | No | Street address |
| `propertyLocation` | string | No | VR location label |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | No | New property ID |
| `type` | string | No | ltr or vr |
**HTTP errors:** 400 validation, 401, 403, 409 duplicate name
#### `PUT` /API/adminProperties/V1/properties/{id}.json
**Auth:** admin
Update whitelisted property fields.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | propertyId |
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyName` | string | No | Mutable name |
| `propertyDesc` | string | No | Description |
| `propertyRate` | string | No | Rate |
| `onWebsite` | int | No | 0 or 1 |
| `isArchived` | int | No | 0 or 1 soft-archive |
**HTTP errors:** 400 unknown fields, 404, 409 duplicate name
#### `PUT` /API/adminProperties/V1/property/{id}.json
**Auth:** admin
Alias for property update.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | propertyId |
**HTTP errors:** 400, 404
#### `DELETE` /API/adminProperties/V1/properties/{id}.json
**Auth:** admin
Soft-archive property (isArchived=1). Blocked when active future VR reservations exist.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | propertyId |
**HTTP errors:** 404, 409 blocking dependencies
#### `DELETE` /API/adminProperties/V1/property/{id}.json
**Auth:** admin
Alias for property archive.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | propertyId |
**HTTP errors:** 404, 409
### Admin Invoices
Controller: adminInvoices · Service: rpm_finance_invoice_service()
#### `GET` /API/adminInvoices/V1/invoices.json
**Auth:** admin
List invoices with optional filters (excludes drafts unless status=9).
**Service:** `InvoiceService::listInvoices()`
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tenantId` | int | No | Filter by tenant |
| `propertyId` | int | No | Filter by property |
| `status` | int | No | 0=Unpaid, 1=Paid, 2=Refunded, 3=Processing, 5=Cancelled, 7=Void, 9=Draft |
| `dateFrom` | string | No | Invoice date >= (YYYY-MM-DD) |
| `dateTo` | string | No | Invoice date <= (YYYY-MM-DD) |
| `search` | string | No | ID, description, tenant name/email |
| `page` | int | No | Page (default 1) |
| `limit` | int | No | Per page (default 20, max 100) |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Invoice summary rows |
| `items[].statusLabel` | string | No | Human-readable status |
| `page` | int | No | Current page |
| `total` | int | No | Total matches |
**HTTP errors:** 401, 403
#### `GET` /API/adminInvoices/V1/invoices/{id}.json
**Auth:** admin
Single invoice with line items and computed totals.
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Invoice ID |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `lineItems` | array | No | invoiceItems rows |
| `subtotalComputed` | float | No | Sum of line items |
| `totalComputed` | float | No | Total with tax |
| `paidComputed` | float | No | Settled payments |
**HTTP errors:** 404 Invoice not found
#### `GET` /API/adminInvoices/V1/invoice/{id}.json
**Auth:** admin
Alias for single invoice.
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Invoice ID |
**HTTP errors:** 404 Invoice not found
#### `POST` /API/adminInvoices/V1/invoices.json
**Auth:** admin
Create invoice with optional line items.
**Service:** `InvoiceService::adminCreateInvoice()`
**Roles:** `super_admin`
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `description` | string | Yes | RENT\|BOOKING\|HOUSEKEEPING\|SERVICE\|DEPOSIT\|ADJUSTMENT\|OWNER |
| `tenantId` | int | No | Invoicee tenant (or use adminId) |
| `adminId` | int | No | Invoicee admin/landlord |
| `propertyId` | int | No | Linked property |
| `dueDate` | string | Yes | YYYY-MM-DD |
| `tax` | float | No | Tax rate % |
| `tax2` | float | No | Second tax rate % |
| `status` | int | No | Invoice status (default 0) |
| `lineItems` | array | No | [{description, amount, quantity, taxed}] |
**HTTP errors:** 400 validation, 401, 403
#### `PUT` /API/adminInvoices/V1/invoices/{id}.json
**Auth:** admin
Update invoice header and append line items.
**Service:** `InvoiceService::adminUpdateInvoice()`
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Invoice ID |
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | int | No | 0–9 status codes |
| `tax` | float | No | Tax rate % |
| `description` | string | No | Invoice type |
| `lineItems` | array | No | Additional line items to append |
**HTTP errors:** 400, 404
**Notes:** Calls rpm_refresh_invoice_totals() and rpm_sync_invoice_payment_state() after mutation.
#### `PUT` /API/adminInvoices/V1/invoice/{id}.json
**Auth:** admin
Alias for invoice update.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Invoice ID |
**HTTP errors:** 404
#### `DELETE` /API/adminInvoices/V1/invoices/{id}.json
**Auth:** admin
Cancel invoice (status=5) when no settled payments. Reverses journal entry.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Invoice ID |
**HTTP errors:** 404, 409 settled payments
#### `DELETE` /API/adminInvoices/V1/invoice/{id}.json
**Auth:** admin
Alias for invoice cancel.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Invoice ID |
**HTTP errors:** 404, 409
### Admin Tenants
Controller: adminTenants · Service: rpm_admin_tenant_service()
#### `GET` /API/adminTenants/V1/tenants.json
**Auth:** admin
List tenants (paginated, searchable by email/name).
**Service:** `TenantService::listTenants()`
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `search` | string | No | Email or name search |
| `isActive` | string | No | 0 or 1 |
| `page` | int | No | Page (default 1) |
| `limit` | int | No | Per page (default 20, max 100) |
**HTTP errors:** 401, 403
#### `GET` /API/adminTenants/V1/tenants/{id}.json
**Auth:** admin
Single tenant profile (decrypted contact fields when available).
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | tenantId |
**HTTP errors:** 404 Tenant not found
#### `GET` /API/adminTenants/V1/tenant/{id}.json
**Auth:** admin
Alias for single tenant.
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | tenantId |
**HTTP errors:** 404 Tenant not found
#### `POST` /API/adminTenants/V1/tenants.json
**Auth:** admin
Create tenant account (bcrypt password).
**Service:** `TenantService::adminCreateTenant()`
**Roles:** `super_admin`
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tenantEmail` | string | Yes | Unique email |
| `tenantFirstName` | string | Yes | First name |
| `tenantLastName` | string | Yes | Last name |
| `password` | string | Yes | Plain password (hashed via rpm_password_hash) |
| `isActive` | int | No | 0 or 1 (default 0) |
| `tenantPhone` | string | No | Encrypted at rest |
**HTTP errors:** 400, 409 duplicate email
#### `PUT` /API/adminTenants/V1/tenants/{id}.json
**Auth:** admin
Update tenant profile fields.
**Service:** `TenantService::adminUpdateTenant()`
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | tenantId |
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tenantFirstName` | string | No | First name |
| `isActive` | int | No | 0 or 1 |
| `password` | string | No | New password (rpm_password_hash) |
**HTTP errors:** 400, 404
#### `PUT` /API/adminTenants/V1/tenant/{id}.json
**Auth:** admin
Alias for tenant update.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | tenantId |
**HTTP errors:** 404
#### `DELETE` /API/adminTenants/V1/tenants/{id}.json
**Auth:** admin
Deactivate tenant (isActive=0). Not a hard SQL delete.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | tenantId |
**HTTP errors:** 404
#### `DELETE` /API/adminTenants/V1/tenant/{id}.json
**Auth:** admin
Alias for tenant deactivate.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | tenantId |
**HTTP errors:** 404
### Admin Reservations
Controller: adminReservations · Service: rpm_booking_reservation_service()
#### `GET` /API/adminReservations/V1/reservations.json
**Auth:** admin
List VR reservations with optional filters.
**Service:** `ReservationService::listReservations()`
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | No | Filter by VR property (serviceID) |
| `tenantId` | int | No | Filter by linked tenant |
| `status` | string | No | Reservation status code |
| `dateFrom` | string | No | Stay start >= (YYYY-MM-DD) |
| `dateTo` | string | No | Stay end <= (YYYY-MM-DD) |
| `search` | string | No | Guest name, email, or reservation ID |
| `page` | int | No | Page (default 1) |
| `limit` | int | No | Per page (default 20, max 100) |
**HTTP errors:** 401, 403
#### `GET` /API/adminReservations/V1/reservations/{id}.json
**Auth:** admin
Single reservation with stay dates.
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Reservation ID |
**HTTP errors:** 404 Reservation not found
#### `GET` /API/adminReservations/V1/reservation/{id}.json
**Auth:** admin
Alias for single reservation.
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Reservation ID |
**HTTP errors:** 404 Reservation not found
#### `POST` /API/adminReservations/V1/reservations.json
**Auth:** admin
Create VR reservation (admin path; no public CAPTCHA).
**Service:** `ReservationService::createReservation()`
**Roles:** `super_admin`
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | VR property (serviceID) |
| `dateFrom` | string | Yes | Stay start YYYY-MM-DD |
| `dateTo` | string | Yes | Stay end YYYY-MM-DD |
| `name` | string | Yes | Guest name |
| `email` | string | Yes | Guest email |
| `totalPrice` | float | No | Booking total |
| `status` | string | No | Reservation status (default 1) |
| `skipAvailabilityCheck` | bool | No | Bypass availability validation |
| `adminBlock` | string | No | Admin block flag |
**HTTP errors:** 400 dates unavailable, 401, 403
#### `PUT` /API/adminReservations/V1/reservations/{id}.json
**Auth:** admin
Update reservation status and/or stay dates.
**Service:** `ReservationService::adminUpdateReservation()`
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Reservation ID |
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | string | No | New status code |
| `dateFrom` | string | No | New stay start |
| `dateTo` | string | No | New stay end |
| `totalPrice` | float | No | Updated total |
**HTTP errors:** 404, 409 dates unavailable
#### `PUT` /API/adminReservations/V1/reservation/{id}.json
**Auth:** admin
Alias for reservation update.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Reservation ID |
**HTTP errors:** 404
#### `DELETE` /API/adminReservations/V1/reservations/{id}.json
**Auth:** admin
Cancel reservation (status=3). Cancels unpaid linked invoices; retains paid invoices.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Reservation ID |
**HTTP errors:** 404, 409 already cancelled
#### `DELETE` /API/adminReservations/V1/reservation/{id}.json
**Auth:** admin
Alias for reservation cancel.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Reservation ID |
**HTTP errors:** 404, 409
### Admin Expenses
Controller: adminExpenses · Service: rpm_finance_expense_service()
#### `GET` /API/adminExpenses/V1/expenses.json
**Auth:** admin
List expenses with filters.
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | No | Filter by property |
| `status` | int | No | 0=Unpaid, 1=Paid, 3=Cancelled, 9=Draft |
| `dateFrom` | string | No | Expense date >= |
| `dateTo` | string | No | Expense date <= |
| `page` | int | No | Page (default 1) |
| `limit` | int | No | Per page (max 100) |
**HTTP errors:** 401, 403
#### `GET` /API/adminExpenses/V1/expenses/{id}.json
**Auth:** admin
Single expense with line items and computed totals.
**Roles:** `super_admin, svc_rep, landlord (read, own properties)`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Expense ID |
**HTTP errors:** 404
#### `POST` /API/adminExpenses/V1/expenses.json
**Auth:** admin
Create expense with optional line items.
**Roles:** `super_admin`
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `description` | string | Yes | RENT\|BOOKING\|HOUSEKEEPING\|SERVICE\|OWNER\|ADJUSTMENT |
| `paytoId` | string | Yes | Payee selector (admin or vendor id) |
| `dueDate` | string | Yes | YYYY-MM-DD |
| `propertyId` | int | No | Linked property |
| `lineItems` | array | No | Line items to append |
**HTTP errors:** 400, 403
#### `PUT` /API/adminExpenses/V1/expenses/{id}.json
**Auth:** admin
Update expense header and append line items.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Expense ID |
**HTTP errors:** 400, 404
#### `DELETE` /API/adminExpenses/V1/expenses/{id}.json
**Auth:** admin
Cancel expense (status=3) when no settled payments.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | Expense ID |
**HTTP errors:** 404, 409 settled payments
### Admin Housekeeping
Controller: adminHousekeeping · Service: rpm_housekeeping_service()
#### `GET` /API/adminHousekeeping/V1/requests.json
**Auth:** admin
List housekeeping requests.
**Roles:** `super_admin, svc_rep`
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | No | Filter by property |
| `status` | int | No | Request status (6=archived excluded by default) |
| `assignedTo` | int | No | Housekeeper adminId |
| `dateFrom` | string | No | Request date >= |
| `dateTo` | string | No | Request date <= |
**HTTP errors:** 401, 403
#### `GET` /API/adminHousekeeping/V1/requests/{id}.json
**Auth:** admin
Single housekeeping request.
**Roles:** `super_admin, svc_rep`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | requestId |
**HTTP errors:** 404
#### `POST` /API/adminHousekeeping/V1/requests.json
**Auth:** admin
Create housekeeping request.
**Roles:** `super_admin`
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | Property ID |
| `requestTitle` | string | Yes | Request title |
| `requestDate` | string | No | Scheduled date |
**HTTP errors:** 400, 403
#### `PUT` /API/adminHousekeeping/V1/requests/{id}.json
**Auth:** admin
Update housekeeping request fields.
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | requestId |
**HTTP errors:** 404
#### `DELETE` /API/adminHousekeeping/V1/requests/{id}.json
**Auth:** admin
Archive request (requestStatus=6).
**Roles:** `super_admin`
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | requestId |
**HTTP errors:** 404, 409 already archived
### Admin Reports
Controller: adminReports · Service: rpm_finance_report_service() — read-only
#### `GET` /API/adminReports/V1/income.json
**Auth:** admin
Income summary from settled transactions (amountin) in date range.
**Roles:** `super_admin, svc_rep`
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `dateFrom` | string | Yes | YYYY-MM-DD |
| `dateTo` | string | Yes | YYYY-MM-DD |
| `propertyId` | int | No | Optional property filter |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `totalIncome` | float | No | Sum of income transactions |
| `transactionCount` | int | No | Matching rows |
**HTTP errors:** 400 missing dates, 403
#### `GET` /API/adminReports/V1/occupancy.json
**Auth:** admin
VR occupancy summary — booked nights per property.
**Roles:** `super_admin, svc_rep`
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `dateFrom` | string | Yes | Stay checkout >= |
| `dateTo` | string | Yes | Stay checkout <= |
| `propertyId` | int | No | Optional VR property filter |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Per-property occupancy rows |
| `totalNights` | int | No | Grand total nights |
**HTTP errors:** 400, 403
### Tenant Authentication
Controller: publicAuth · PHP session cookie (Secure, HttpOnly, SameSite=Lax). Requires HTTPS in production; set RPM_FORCE_HTTPS=1 to redirect HTTP.
#### `POST` /API/publicAuth/V1/login.json
**Auth:** public
Authenticate tenant; sets session (tenantId, propertyId, leaseId).
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `email` | string | Yes | tenantEmail alias accepted |
| `password` | string | Yes | Plain password |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | bool | No | true |
| `user.tenantId` | int | No | Tenant ID |
| `user.tenantEmail` | string | No | Email |
| `user.tenantFirstName` | string | No | First name |
| `user.tenantLastName` | string | No | Last name |
**Example request:**
```json
{ "email": "guest@example.com", "password": "your-password" }
```
**Example response:**
```json
{
"success": true,
"user": {
"tenantId": 15,
"tenantEmail": "guest@example.com",
"tenantFirstName": "Jane",
"tenantLastName": "Guest"
}
}
```
**HTTP errors:** 400 missing fields, 401 invalid credentials, 403 inactive account, 429 lockout (5 failures / 15 min per IP and email)
#### `POST` /API/publicAuth/V1/logout.json
**Auth:** public
Destroy tenant session.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | bool | No | true |
#### `GET` /API/publicAuth/V1/me.json
**Auth:** session
Current tenant session data.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tenantId` | int | No | Session tenant |
| `tenantEmail` | string | No | Email |
| `tenantFirstName` | string | No | First name |
| `tenantLastName` | string | No | Last name |
| `propertyId` | int | No | Linked property |
| `leaseId` | int | No | Active lease |
**HTTP errors:** 401 Not authenticated
#### `POST` /API/publicAuth/V1/register.json
**Auth:** public
Register new tenant; sends activation email.
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tenantFirstName` | string | Yes | First name |
| `tenantLastName` | string | Yes | Last name |
| `tenantEmail` | string | Yes | newEmail alias |
| `password` | string | Yes | password1 alias |
| `password2` | string | No | Confirmation |
| `tenantAddress` | string | No | Encrypted at rest |
| `tenantCity` | string | No | Encrypted |
| `tenantState` | string | No | Encrypted |
| `tenantZip` | string | No | Encrypted |
| `tenantCountry` | string | No | Encrypted |
| `tenantPhone` | string | No | Encrypted |
| `tenantAltPhone` | string | No | Encrypted |
| `applyToLeaseId` | int | No | Post-activation lease redirect |
| `isEmpty` | string | No | Honeypot — must be empty |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | bool | No | true |
| `activateUrl` | string | No | Activation link sent by email |
**HTTP errors:** 400 validation, 409 email exists, 500 registration failure
#### `GET` /API/publicAuth/V1/activate.json
**Auth:** public
Activate account via email link; starts session.
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tenantEmail` | string | Yes | Account email |
| `hash` | string | Yes | Activation token |
| `applyToLeaseId` | int | No | Optional lease redirect |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | bool | No | true |
| `user` | object | No | Tenant profile |
| `applyToLeaseId` | int | No | Echo of query param |
**HTTP errors:** 400 invalid link, 429 rate limited (10/hour per IP)
### VR Properties
Controller: vrProperties · Service: CatalogService (Vrproperties table)
#### `GET` /API/vrProperties/V1/vrProperties.json
**Auth:** public
List vacation rental properties visible on website.
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `page` | int | No | Page number (default 1) |
| `limit` | int | No | Per page (default 20, max 100) |
| `beds` | string | No | Bedroom filter (alias bedrooms) |
| `baths` | string | No | Bathroom filter |
| `location` | string | No | Address search (max 128 chars) |
| `search` | string | No | Name/address search (max 128 chars) |
| `type` | string | No | propertyType filter |
| `pets` | string | No | 1 = pets allowed only |
| `featured` | string | No | 1 = featured listings only |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Property objects |
| `page` | int | No | Current page |
| `limit` | int | No | Page size |
| `total` | int | No | Total matching properties |
| `items[].propertyId` | int | No | ID (>1999 for VR) |
| `items[].propertyName` | string | No | Name |
| `items[].propertyDesc` | string | No | Description HTML |
| `items[].propertyAddress` | string | No | Address |
| `items[].propertyLocation` | string | No | Location label |
| `items[].propertyRate` | float | No | Nightly rate |
| `items[].propertyWeekendRate` | float | No | Weekend rate |
| `items[].propertyDeposit` | float | No | Security deposit |
| `items[].petsAllowed` | string | No | Yes/No |
| `items[].propertyAmenities` | string | No | Amenities list |
| `items[].bedrooms` | string | No | Bedroom count |
| `items[].bathrooms` | string | No | Bathroom count |
| `items[].propertyBookingFee` | float | No | Booking fee |
| `items[].housekeepingFee` | float | No | Cleaning fee |
| `items[].petFee` | float | No | Per-pet fee |
| `items[].featuredImage` | string | No | Primary image filename |
| `items[].isFeatured` | int | No | Featured flag |
| `items[].seoTitle` | string | No | SEO title |
| `items[].seoDesc` | string | No | SEO description |
**Example response:**
```json
{
"items": [{ "propertyId": 2001, "propertyName": "Lake View Cabin", "propertyRate": 189.00, "bedrooms": "3", "featuredImage": "lake.jpg" }],
"page": 1,
"limit": 20,
"total": 45
}
```
#### `GET` /API/vrProperties/V1/vrProperties/{id}.json
**Auth:** public
Single VR property with all catalog fields.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | propertyId |
**HTTP errors:** 404 Property not found
#### `GET` /API/vrProperties/V1/vrPropertyImages.json
**Auth:** public
All VR property images.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `pictureId` | int | No | Image ID |
| `propertyId` | int | No | Property FK |
| `pictureName` | string | No | Alt text |
| `pictureUrl` | string | No | Filename |
| `isFeatured` | int | No | 1 = featured |
| `pictureOrder` | int | No | Sort order |
| `pictureDate` | string | No | Formatted upload date |
#### `GET` /API/vrProperties/V1/vrPropertyImages/{propertyId}.json
**Auth:** public
Images for one VR property.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | Property ID |
### LTR Properties
Controller: ltrProperties · properties table
#### `GET` /API/ltrProperties/V1/ltrProperties.json
**Auth:** public
List long-term rental properties (same filters as VR).
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `page` | int | No | Page (default 1) |
| `limit` | int | No | Per page (max 100) |
| `beds` | string | No | Bedroom filter |
| `search` | string | No | Search term (max 128 chars) |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | LTR properties (no VR-only fields) |
| `items[].propertyId` | int | No | ID |
| `items[].propertyName` | string | No | Name |
| `items[].propertyRate` | float | No | Monthly rent |
| `items[].propertyDeposit` | float | No | Deposit |
| `items[].availOn` | string | No | Available date |
| `items[].featuredImage` | string | No | Primary image |
#### `GET` /API/ltrProperties/V1/ltrProperties/{id}.json
**Auth:** public
Single LTR property.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | propertyId |
**HTTP errors:** 404 Property not found
#### `GET` /API/ltrProperties/V1/ltrPropertyImages/{propertyId}.json
**Auth:** public
Images from propertypictures table.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | Property ID |
### Public Booking
Controller: publicBooking · Booking PSR-4 services
#### `GET` /API/publicBooking/V1/availability/{propertyId}.json
**Auth:** public
Check if date range is bookable with pricing preview.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | VR property ID |
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `dateFrom` | string | Yes | YYYY-MM-DD (alias from) |
| `dateTo` | string | Yes | YYYY-MM-DD (alias to) |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `available` | bool | No | true if bookable |
| `message` | string | No | Reason if unavailable |
| `price` | float | No | Total stay price when available |
| `nightRates` | array | No | Per-night breakdown |
| `intervals` | int | No | Night count |
| `spots` | int | No | Remaining capacity |
**Example request:**
```json
GET /API/publicBooking/V1/availability/2001.json?dateFrom=2026-08-01&dateTo=2026-08-07
```
**Example response:**
```json
{
"available": true,
"message": "",
"price": 1134.00,
"nightRates": [{ "date": "2026-08-01", "rate": 189.00 }],
"intervals": 6,
"spots": 1
}
```
**HTTP errors:** 400 missing propertyId or dates
#### `GET` /API/publicBooking/V1/calendar/{propertyId}.json
**Auth:** public
Calendar data: blocked ranges and per-day prices.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | VR property |
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `months` | int | No | Forward months (default 12, max 24) |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | No | Property |
| `today` | string | No | Server date YYYY-MM-DD |
| `until` | string | No | Range end date |
| `ranges` | array | No | Blocked/reserved date ranges |
| `baseRate` | float | No | Default nightly rate |
| `weekendRate` | float | No | Weekend rate |
| `dayPrices` | object | No | Map of date → price |
**Example response:**
```json
{
"propertyId": 2001,
"today": "2026-07-13",
"until": "2027-07-13",
"ranges": [{ "start": "2026-08-10", "end": "2026-08-15", "type": "reservation" }],
"baseRate": 189.00,
"weekendRate": 225.00,
"dayPrices": { "2026-08-01": 189.00, "2026-08-02": 225.00 }
}
```
#### `POST` /API/publicBooking/V1/quote.json
**Auth:** public
Full quote with fees, taxes, and coupon.
**Service:** `QuoteCalculator::buildQuote()`
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | serviceID alias |
| `dateFrom` | string | Yes | Check-in YYYY-MM-DD |
| `dateTo` | string | Yes | Check-out YYYY-MM-DD |
| `adults` | int | No | Guest count (bAdults alias, default 1) |
| `children` | int | No | bChildren alias |
| `pets` | int | No | bPets alias |
| `couponCode` | string | No | Optional discount code |
| `extraRooms` | array | No | Additional room prices |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | No | Property |
| `dateFrom` | string | No | Check-in |
| `dateTo` | string | No | Check-out |
| `nights` | int | No | Night count |
| `subtotal` | float | No | Room subtotal |
| `discount` | float | No | Coupon discount |
| `couponApplied` | bool | No | Coupon valid |
| `cleaningFee` | float | No | Housekeeping |
| `damageWaiver` | float | No | Damage waiver |
| `bookingFee` | float | No | Booking fee |
| `peopleFees` | float | No | Extra guest fees |
| `petFee` | float | No | Pet fees |
| `taxRate` | float | No | Combined tax % |
| `tax` | float | No | Tax amount |
| `total` | float | No | Grand total |
| `lineItems` | array | No | { label, amount } breakdown |
| `nightRates` | array | No | Per-night rates |
**Example request:**
```json
{
"propertyId": 2001,
"dateFrom": "2026-08-01",
"dateTo": "2026-08-07",
"adults": 2,
"children": 0,
"pets": 0,
"couponCode": "SUMMER10"
}
```
**Example response:**
```json
{
"propertyId": 2001,
"nights": 6,
"subtotal": 1134.00,
"discount": 113.40,
"couponApplied": true,
"cleaningFee": 150.00,
"tax": 98.50,
"total": 1269.10,
"lineItems": [
{ "label": "6 nights", "amount": 1134.00 },
{ "label": "Discount", "amount": -113.40 },
{ "label": "Cleaning fee", "amount": 150.00 }
]
}
```
#### `POST` /API/publicBooking/V1/validateCoupon.json
**Auth:** public
Validate coupon without full quote.
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | Property |
| `couponCode` | string | Yes | Code to check |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `valid` | bool | No | Coupon accepted |
| `type` | string | No | abs or percent |
| `value` | float | No | Discount value |
| `message` | string | No | Status message |
#### `POST` /API/publicBooking/V1/book.json
**Auth:** public
Create reservation when tenant session or verified guest email + CAPTCHA.
**Service:** `ApiSecurityPolicy::assertBookingAllowed(), ReservationService::createReservation()`
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | VR property |
| `dateFrom` | string | Yes | Check-in (max 10 chars) |
| `dateTo` | string | Yes | Check-out (max 10 chars) |
| `name` | string | No | Guest name (max 128) |
| `email` | string | No | Guest email — required for guest booking |
| `captchaToken` | string | No | Turnstile/reCAPTCHA token when provider is not builtin |
| `captcha` | string | No | Builtin math answer when apiCaptchaProvider=builtin |
| `captcha_sum` | string | No | md5(answer) for builtin provider |
| `phone` | string | No | Phone (max 40) |
| `address` | string | No | bAddress alias |
| `city` | string | No | bCity alias |
| `state` | string | No | bState alias |
| `zip` | string | No | bZip alias |
| `adults` | int | No | Guest count |
| `children` | int | No | Children count |
| `pets` | int | No | Pet count |
| `couponCode` | string | No | Coupon |
| `tenantId` | int | No | Linked tenant if logged in |
| `extraRooms` | array | No | Extra room selections |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | bool | No | true when reservation created |
| `requiresPortal` | bool | No | true if name/email missing |
| `orderId` | int | No | Booking order ID |
| `invoiceId` | int|null | No | Invoice if total > 0 |
| `tenantId` | int | No | Associated tenant |
| `quote` | object | No | Full quote object |
| `message` | string | No | Next-step instructions |
**Example request:**
```json
{
"propertyId": 2001,
"dateFrom": "2026-08-01",
"dateTo": "2026-08-07",
"name": "Jane Guest",
"email": "jane@example.com",
"phone": "555-0100",
"adults": 2
}
```
**Example response:**
```json
{
"success": true,
"orderId": 8842,
"invoiceId": 1205,
"tenantId": 0,
"quote": { "total": 1269.10 },
"message": "Reservation and invoice created. Complete payment via the guest portal."
}
```
**Notes:** Requires tenant session OR guest email + CAPTCHA per apiPolicy in settings.json. Rate limited (429). Without name/email returns { requiresPortal: true, quote } directing to users/booking-home.html.
### Lease Applications
#### `POST` /API/leaseApplications/V1/apply.json
**Auth:** session
Submit LTR lease application for logged-in tenant.
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | Yes | LTR property to apply for |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | bool | No | true |
| `applicationId` | int | No | leaseApplications insert ID |
**HTTP errors:** 401 Login required, 400 missing propertyId
### Site Settings
Controller: siteSettings · Service: rpm_site_settings_service()
#### `GET` /API/siteSettings/V1/settings.json
**Auth:** public
Returns public site configuration and API security policy (secrets excluded).
**Service:** `SiteSettingsService::settings(), ApiSecurityPolicy::publicPolicy()`
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `installUrl` | string | No | Site base URL |
| `logoUrl` | string | No | Logo path/URL |
| `siteName` | string | No | Display name |
| `businessName` | string | No | Legal business name |
| `businessAddress` | string | No | Business address |
| `businessEmail` | string | No | Contact email |
| `businessPhone` | string | No | Business phone |
| `businessTaxId` | string | No | Tax ID — only when apiExposeBusinessTaxId enabled in admin |
| `contactPhone` | string | No | Public contact phone |
| `localization` | string | No | Locale code |
| `paymentCurrency` | string | No | Currency code |
| `siteType` | string | No | Site type |
| `siteCategory` | string | No | Site category |
| `systemTimeZone` | string | No | PHP timezone |
| `systemDateFormat` | string | No | Date format |
| `vrCheckInTime` | string | No | Default VR check-in |
| `vrCheckOutTime` | string | No | Default VR check-out |
| `paymentCompleteMsg` | string | No | Payment success message |
| `paymentEmail` | string | No | Payment notification email |
| `uploadPath` | string | No | Only when apiExposeInternalPaths legacy opt-in |
| `VruploadPath` | string | No | Only when apiExposeInternalPaths legacy opt-in |
| `avatarFolder` | string | No | Only when apiExposeInternalPaths legacy opt-in |
| `propertyPicsPath` | string | No | Only when apiExposeInternalPaths legacy opt-in |
| `VrpropertyPicsPath` | string | No | Only when apiExposeInternalPaths legacy opt-in |
| `apiPolicy` | object | No | Public booking/CAPTCHA policy (no secrets) |
| `apiPolicy.apiBookingGuestEnabled` | int | No | 1 = guest checkout allowed |
| `apiPolicy.apiBookingRequireLogin` | int | No | 1 = tenant session required for book.json |
| `apiPolicy.apiCaptchaProvider` | string | No | builtin \| turnstile \| recaptcha_v2 \| recaptcha_v3 |
| `apiPolicy.apiCaptchaSiteKey` | string | No | Public CAPTCHA site key |
| `apiPolicy.apiContactCaptchaEnabled` | int | No | 1 = contact POST requires CAPTCHA |
| `apiPolicy.apiSubscribeCaptchaEnabled` | int | No | 1 = subscribe POST requires CAPTCHA |
**Example request:**
```json
GET /API/siteSettings/V1/settings.json
Accept: application/json
```
**Example response:**
```json
{
"installUrl": "https://demo.rpm-pro.com/",
"siteName": "My Rentals",
"businessEmail": "info@example.com",
"paymentCurrency": "USD",
"systemTimeZone": "America/New_York",
"vrCheckInTime": "16:00",
"vrCheckOutTime": "10:00",
"apiPolicy": {
"apiBookingGuestEnabled": 1,
"apiBookingRequireLogin": 0,
"apiCaptchaProvider": "builtin",
"apiCaptchaSiteKey": "",
"apiContactCaptchaEnabled": 0,
"apiSubscribeCaptchaEnabled": 0
}
}
```
### Site Content
Controller: siteContent · CMS blocks from siteContent table
#### `GET` /API/siteContent/V1/content.json
**Auth:** public
All CMS content blocks (single row, all columns).
**Service:** `SiteSettingsService::siteContent()`
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `indexHeader` | string | No | Homepage header HTML |
| `indexSlogan` | string | No | Homepage slogan |
| `indexPageTitle` | string | No | Homepage SEO title |
| `indexPageDesc` | string | No | Homepage meta description |
| `indexPageKeywords` | string | No | Homepage keywords |
| `c2aText` | string | No | Call-to-action text |
| `c2aButtonText` | string | No | CTA button label |
| `c2aUrl` | string | No | CTA link URL |
| `whyUsHeader` | string | No | Why-us section header |
| `whyUsSlogan` | string | No | Why-us slogan |
| `whyUsH2` | string | No | Why-us subheading |
| `whyUsText` | string | No | Why-us body (HTML) |
| `keyPoint1..6` | string | No | Feature bullet points |
| `keyPointButtonText` | string | No | Feature section button |
| `keyPointButtonUrl` | string | No | Feature section URL |
| `indexExtra..4` | string | No | Additional homepage slots |
**Example response:**
```json
{
"indexHeader": "Welcome to Lake Chatuge Rentals",
"indexSlogan": "Your mountain getaway",
"c2aText": "Browse our properties",
"c2aButtonText": "View Rentals",
"c2aUrl": "/vacation-rental-properties.html"
}
```
**Notes:** CMS fields may contain trusted admin HTML. Headless consumers must sanitize before rendering (e.g. DOMPurify) or use a strict Content-Security-Policy.
#### `GET` /API/siteContent/V1/content/{key}.json
**Auth:** public
Single content block by column key.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | string | Yes | siteContent column name, e.g. indexHeader |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `{key}` | string | No | Value of the requested column |
**Example request:**
```json
GET /API/siteContent/V1/content/indexHeader.json
```
**Example response:**
```json
{ "indexHeader": "Welcome to our rentals" }
```
### Pages
Controller: sitePages
#### `GET` /API/sitePages/V1/pages.json
**Auth:** public
List active static pages.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Array of page objects |
| `items[].pageId` | int | No | Primary key |
| `items[].pageTitle` | string | No | Page title |
| `items[].pageUrl` | string | No | URL slug |
| `items[].pageDesc` | string | No | Meta description |
| `items[].pageImage` | string | No | Featured image path |
| `items[].dateUpdated` | string | No | Last update timestamp |
**Example response:**
```json
{
"items": [
{
"pageId": 11,
"pageTitle": "Whitewater Rafting",
"pageUrl": "whitewater-rafting",
"pageDesc": "Local adventures",
"pageImage": "",
"dateUpdated": "2024-06-01 12:00:00"
}
]
}
```
#### `GET` /API/sitePages/V1/pages/{slug}.json
**Auth:** public
Full page by URL slug.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `slug` | string | Yes | pageUrl value |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `pageId` | int | No | Primary key |
| `pageTitle` | string | No | Title |
| `pageContent` | string | No | HTML body |
| `pageUrl` | string | No | Slug |
| `pageKeywords` | string | No | SEO keywords |
| `pageDesc` | string | No | Meta description |
| `pageImage` | string | No | Image path |
| `dateUpdated` | string | No | Updated timestamp |
**HTTP errors:** 404 Page not found
**Notes:** Slug must match ^[a-z0-9-]{1,64}$ (400 otherwise). pageContent is trusted admin HTML — sanitize on render.
### Reviews
Controller: siteReviews
#### `GET` /API/siteReviews/V1/reviews.json
**Auth:** public
Property reviews with optional filters.
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `propertyId` | int | No | Filter by property |
| `limit` | int | No | Max results (default 20, max 50) |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Review list |
| `items[].reviewId` | int | No | Review ID |
| `items[].reviewTitle` | string | No | Title |
| `items[].reviewText` | string | No | Review body |
| `items[].r1..r6` | int | No | Rating dimensions (1-5) |
| `items[].dateCreated` | string | No | Created date |
| `items[].propertyId` | int | No | Related property |
| `items[].tenantFirstName` | string | No | Reviewer first name |
| `items[].tenantLastName` | string | No | Reviewer last name |
**Example request:**
```json
GET /API/siteReviews/V1/reviews.json?propertyId=42&limit=10
```
**Example response:**
```json
{
"items": [
{
"reviewId": 5,
"reviewTitle": "Great stay",
"reviewText": "Beautiful property...",
"r1": 5, "r2": 5, "r3": 5, "r4": 5, "r5": 5, "r6": 5,
"propertyId": 42,
"tenantFirstName": "John",
"tenantLastName": "Doe",
"dateCreated": "2025-07-01 10:00:00"
}
]
}
```
### Sliders
#### `GET` /API/siteSliders/V1/sliders.json
**Auth:** public
Homepage slider/carousel items.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Slider list |
| `items[].sliderId` | int | No | ID |
| `items[].sliderUrl` | string | No | Background image URL |
| `items[].sliderHeader` | string | No | Headline |
| `items[].sliderText` | string | No | Subtext |
| `items[].sliderButtonText` | string | No | Button label |
| `items[].sliderButtonUrl` | string | No | Button link |
### Navigation
#### `GET` /API/siteNavigation/V1/menus.json
**Auth:** public
Site navigation menus with nested children.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `menus` | array | No | Primary nav from menus table |
| `menus[].menuId` | int | No | Menu ID |
| `menus[].menuTitle` | string | No | Label |
| `menus[].menuUrl` | string | No | Link URL |
| `menus[].menuFor` | int | No | 1=custom, 2=blog, 3=pages |
| `menus[].children` | array | No | { title, url } sub-items |
| `links` | array | No | Default footer/quick links |
### FAQ
#### `GET` /API/siteFaq/V1/faqs.json
**Auth:** public
Active FAQ entries.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | FAQ list |
| `items[].faqId` | int | No | ID |
| `items[].faqQuestion` | string | No | Question (faqName alias) |
| `items[].faqAnswer` | string | No | Answer HTML (faqContent alias) |
| `items[].dateUpdated` | string | No | Updated |
### Events
#### `GET` /API/siteEvents/V1/events.json
**Auth:** public
List active events.
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `limit` | int | No | Default 50, max 100 |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Event list |
| `items[].eventId` | int | No | ID |
| `items[].eventTitle` | string | No | Title |
| `items[].eventUrl` | string | No | Slug/URL |
| `items[].eventCategory` | string | No | Category |
| `items[].eventDateStart` | string | No | Start date |
| `items[].eventDateEnd` | string | No | End date |
| `items[].eventImage` | string | No | Image path |
#### `GET` /API/siteEvents/V1/events/{id}.json
**Auth:** public
Single event with full content.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | eventId |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `eventId` | int | No | ID |
| `eventTitle` | string | No | Title |
| `eventContent` | string | No | Full HTML body |
| `eventDesc` | string | No | Short description |
| `eventKeywords` | string | No | SEO keywords |
| `eventDateStart` | string | No | Start |
| `eventDateEnd` | string | No | End |
| `eventImage` | string | No | Image |
| `adminId` | int | No | Author admin |
**HTTP errors:** 404 Event not found
### Contact & Leads
Controller: siteContact · Service: rpm_site_lead_service()
#### `POST` /API/siteContact/V1/message.json
**Auth:** public
Submit website contact form; stores message and optional lead.
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `senderName` | string | Yes | Sender full name |
| `senderEmail` | string | Yes | Reply email |
| `messageContent` | string | Yes | Message body |
| `messageSubject` | string | No | Subject (default: Website inquiry) |
| `senderPhone` | string | No | Optional phone (creates lead) |
| `Url` | string | No | Honeypot — must be empty |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | bool | No | true on success |
| `messageId` | int | No | messages table insert ID |
**Example request:**
```json
POST /API/siteContact/V1/message.json
Content-Type: application/json
{
"senderName": "Jane Smith",
"senderEmail": "jane@example.com",
"senderPhone": "555-0100",
"messageSubject": "Booking inquiry",
"messageContent": "I would like information about property 42."
}
```
**Example response:**
```json
{ "success": true, "messageId": 128 }
```
**HTTP errors:** 400 validation failure (missing fields or honeypot triggered)
#### `POST` /API/siteContact/V1/subscribe.json
**Auth:** public
Newsletter / mailing list signup.
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `email` | string | Yes | Subscriber email |
| `name` | string | No | Display name (default: Subscriber) |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `success` | bool | No | true on success |
**Example request:**
```json
{ "email": "subscribe@example.com", "name": "Newsletter Reader" }
```
**Example response:**
```json
{ "success": true }
```
### Blog
Controller: networkBlog · BlogService
#### `GET` /API/networkBlog/V1/networkBlogPosts.json
**Auth:** public
List blog posts (excerpted content).
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Posts |
| `items[].postId` | int | No | ID |
| `items[].postTitle` | string | No | Title |
| `items[].postDesc` | string | No | Summary |
| `items[].postContent` | string | No | First 300 chars |
| `items[].postUrl` | string | No | Slug |
| `items[].postCategory` | int | No | Category ID |
| `items[].postImage` | string | No | Featured image |
| `items[].dateCreated` | string | No | Published date |
#### `GET` /API/networkBlog/V1/networkBlogPosts/{id}.json
**Auth:** public
Full blog post.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | postId |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `postId` | int | No | ID |
| `postTitle` | string | No | Title |
| `postContent` | string | No | Full HTML body |
| `postDesc` | string | No | Summary |
| `postUrl` | string | No | Slug |
| `postCategory` | int | No | Category |
| `postImage` | string | No | Image |
| `dateCreated` | string | No | Date |
**HTTP errors:** 404 Post not found
#### `GET` /API/networkBlog/V1/networkBlogByCat/{categoryId}.json
**Auth:** public
Posts filtered by category.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `categoryId` | int | Yes | Category ID |
#### `GET` /API/networkBlog/V1/networkBlogCategory.json
**Auth:** public
List categories.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `categoryId` | int | No | ID |
| `categoryName` | string | No | Name |
| `isActive` | int | No | Active flag |
### Attractions
#### `GET` /API/siteAttractions/V1/attractions.json
**Auth:** public
List attractions, optionally by category.
### Query parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `category` | int | No | Category ID (alias categoryId) |
| `limit` | int | No | Default 100, max 200 |
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Attractions |
| `items[].attractionId` | int | No | ID |
| `items[].attractionTitle` | string | No | Title |
| `items[].attractionUrl` | string | No | Slug |
| `items[].attractionContent` | string | No | Body HTML |
| `items[].attractionDesc` | string | No | Summary |
| `items[].attractionImage` | string | No | Image |
| `items[].categoryId` | int | No | Category FK |
| `items[].categoryName` | string | No | Category label |
#### `GET` /API/siteAttractions/V1/attractions/{id}.json
**Auth:** public
Single attraction.
### Path parameters
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | int | Yes | attractionId |
**HTTP errors:** 404 Attraction not found
#### `GET` /API/siteAttractions/V1/categories.json
**Auth:** public
Attraction categories.
### Response fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `items` | array | No | Categories |
| `items[].categoryId` | int | No | ID |
| `items[].categoryName` | string | No | Name |
| `items[].categoryDesc` | string | No | Description |
| `items[].isActive` | int | No | Active flag |
### Integration scripts (standalone PHP)
Not wlRest — separate files under `/API/`.
#### `POST` /API/stripe-create-intent.html
**Auth:** csrf
Create Stripe PaymentIntent for invoice payment.
### Request body (JSON)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `invoiceId` | int | Yes | Invoice to pay |
| `stripe_csrf` | string | Yes | CSRF token |
**HTTP errors:** 403 invalid CSRF, 400 invalid invoice
#### `POST` /API/stripe-webhook.html
**Auth:** webhook
Stripe webhook handler (payment_intent.succeeded, refunds, disputes).
#### `GET` /API/quickbooks/oauth-start.html
**Auth:** admin
Start QuickBooks OAuth — requires myAdmin session.
#### `POST` /API/bookingpal/webhook.html
**Auth:** webhook
BookingPal inbound OTA reservations.
## JavaScript client
Public website client: `website/src/js/api.js` (`RpmApi`)
```javascript
RpmApi.settings();
RpmApi.vrProperty(2001);
RpmApi.quote({ propertyId: 2001, dateFrom: '2026-08-01', dateTo: '2026-08-07' });
RpmApi.login({ email: 'guest@example.com', password: 'secret' });
```
## Coding standards (API work)
- Thin controllers — validate input, delegate to `rpm_*_service()`
- New logic in `src/RPM/` with factory in appropriate bootstrap file
- Prepared statements for user input
- `rpm_money_cmp()` for currency comparisons
- CSRF on portal POST mutations; Stripe uses separate `stripe_csrf` namespace
---
_End of RPM-PRO API agent reference v5.9.3.3_