Hotelia API is a multi-tenant RESTful API backend for a Hotel Property Management System (PMS). Built with Laravel 13, it supports hotel operations β from reservations and front-desk management to billing, housekeeping, maintenance, dynamic pricing, and real-time audit trails.
- Overview
- Project Status
- Features
- Technology Stack
- Installed Packages
- Architecture
- Folder Structure
- Database
- API
- Security
- Events & Listeners
- Notifications
- Queues & Scheduled Jobs
- Installation
- Environment Variables
- Running Tests
- API Documentation
- Development Workflow
- Roadmap
- Contributing
- License
- Author
Hotelia API solves a core problem in the hospitality industry: fragmented, expensive, or poorly integrated property management tools. It provides a single, cohesive REST API that powers all hotel operations from a central backend.
- Manages multiple hotels and their staff under one system (multi-tenancy via hotel-user pivot)
- Handles the full guest journey: reservation β check-in β check-out β invoicing β payment
- Automates housekeeping workflows triggered by guest check-out
- Calculates dynamic nightly rates via rate plans and pricing rules
- Exposes a room availability matrix with per-night pricing for date ranges
- Tracks maintenance requests tied to specific rooms
- Generates financial reports including ADR and RevPAR KPIs
- Enforces fine-grained, role-based access control across every endpoint
- Records a full, tamper-evident audit log of every state change
| User | Role |
|---|---|
| Hotel Chains | Multi-property centralized management |
| Boutique Hotels | Front desk, billing, and housekeeping automation |
| System Integrators | Headless API to power custom front-ends or mobile apps |
| Developers | Clean codebase to extend or white-label |
- Reduce manual front-desk errors via structured workflows and server-side validation
- Improve occupancy reporting with real-time dashboard stats and revenue KPIs
- Ensure data security and compliance through audit logging and access control
- Provide a scalable, headless API consumed by any client (web, mobile, kiosk)
Hotelia API is a headless backend β there is no admin UI, guest portal, or mobile app in this repository. The API layer is substantially complete; the remaining work is mostly integrations, a frontend, and production operations.
| Scope | Completion | Notes |
|---|---|---|
| Core backend API | ~78% | Auth, hotels, rooms, bookings, billing, housekeeping, maintenance, reports, pricing, availability |
| Full commercial product | ~45% | Requires UI, payment gateways, OTA/channel sync, onboarding, and deployment tooling |
| Test suite | 231 tests | Feature, unit, job, and performance coverage (run composer run test) |
See the Roadmap for what is implemented vs. still planned.
- Token-based authentication via Laravel Sanctum
- Login with account lockout (locked after 3 failed attempts, 15-minute cooldown)
- Logout (current device) and Logout All (all devices)
- Token refresh without requiring re-login
- Password change with history enforcement (last 5 passwords blocked)
- Forgot password / Reset password via signed tokenized email link
- Login history tracking per user (IP address, user agent, timestamps)
- Active session status endpoint (
GET /auth/status)
- Role-Based Access Control (RBAC) via Spatie Laravel Permission
- Five defined roles:
super_admin,hotel_manager,receptionist,housekeeper,accountant - 45 granular permissions (e.g.,
view bookings,check in guests,manage rate plans) - Permission-based middleware on every protected route
- Laravel Policies for object-level authorization (e.g., ensuring a booking belongs to the correct hotel)
- Cross-tenant route binding protection via scoped bindings
- Full CRUD for hotels (name, slug, email, phone, country, city, address, description, logo)
- Soft deletes with cascade resource cleanup
- Hotel-to-user membership management (many-to-many)
- User unlock functionality (admin unlocks locked accounts)
- Activity logging on all hotel changes
- Per-hotel configurable settings: currency, timezone, check-in/check-out time, tax rate
- Configurable booking and invoice number prefixes
- Late checkout fee and early check-in fee configuration
- Booking cancellation window (hours) and overbooking flag
- Language preference per hotel
- Grace period for checkout (minutes)
- Room Types with base pricing, capacity, and amenity associations (many-to-many)
- Rooms linked to room types per hotel with unique room number constraint per hotel
- Room status lifecycle:
availableβoccupiedβcleaningβavailable(ormaintenance) - Room status history tracking
- Global Amenities catalog (CRUD) shared across the system
- Room type β amenity pivot relationships
- Full guest profile management (name, email, phone, nationality, ID document details)
- Guests scoped to hotel context via booking relationships
- Soft deletes with activity logging
- Create bookings with pessimistic locking to prevent double-bookings under concurrent requests
- Date-range overlap detection before any room assignment
- Automatic booking reference number generation with hotel-specific prefix
- Optional rate plan attachment per booking (BAR, corporate, group, etc.)
- Automatic total amount computation via
PricingService(nightly rate Γ nights + ancillary services) - Booking status lifecycle:
pendingβconfirmedβchecked_inβchecked_out|cancelled|no_show - Booking status history automatically recorded on every transition
- Attach ancillary services to bookings with quantity and price
- Check-in: updates booked rooms to
occupied - Check-out: updates rooms to
cleaningand auto-creates housekeeping tasks - Booking cancellation with event dispatch
- Log payments against bookings (cash, card, M-Pesa, bank transfer)
- Payment status management (
pending,completed,failed,refunded) - Invoice auto-reconciliation after each payment
- Auto-generate invoice on demand or retrieve existing one
- Invoice items: room stay line items + service line items
- Tax-inclusive total calculation using hotel's configured tax rate
- Invoice status auto-calculated:
unpaidβpartialβpaid - Invoice number generation with hotel-specific prefix
- Invoice regeneration endpoint (recomputes all line items)
- PDF invoice download via DomPDF (
GET .../invoice/pdf)
- Named rate plans per hotel (code, modifier type, cancellation policy, meal plan)
- Percentage or fixed price modifiers applied on top of room type base price
- Pricing rules with date ranges, day-of-week filters, min/max stay, and priority ordering
- Modifier types:
override,percentage, orfixedadjustments - Rules can be scoped globally, to a rate plan, or to a specific room type
- Rate plan selection integrated into booking creation and availability pricing
- Availability matrix endpoint:
GET /api/v1/hotels/{hotel}/availability - Per room type, per day: total rooms, booked count, available count, and calculated nightly price
- Filterable by date range, room type, and rate plan
- Uses active booking overlap logic (excludes cancelled and no-show bookings)
- Guest booking emails: confirmation, update, cancellation, and no-show
- Billing emails: invoice generated and invoice paid
- Password reset via signed tokenized email link (Laravel
ResetPassword+FRONTEND_URL) - Blade email templates under
resources/views/emails/ - Triggered by domain events through dedicated mail listeners (configure SMTP in
.env)
- Invoice PDF generation with line items, tax breakdown, and hotel branding fields
- Payment receipt PDF download per completed payment
- Rendered via barryvdh/laravel-dompdf with Blade templates under
resources/views/pdf/
- Create, assign, update, and delete housekeeping tasks per room
- Task statuses:
pendingβin_progressβcompleted - Auto-creates tasks on guest checkout (triggered by
BookingService::checkOut) - On task completion: room automatically set to
available(unless active maintenance exists) - Notifications dispatched on task creation and updates
- Maintenance requests per room with priority and status tracking
- Statuses:
openβin_progressβresolved|closed - Room status set to
maintenancewhen active requests exist - Notifications dispatched on request creation
- Integrated with housekeeping: completed cleaning defers to
maintenancestatus if requests are open
- Database-channel notifications for bookings, housekeeping, and maintenance events
- Notification listing and mark-as-read endpoints
BaseNotificationabstract class for consistent notification structure- Notification types managed via
NotificationTypesconstants class
- Automatic activity logging on all major models via
LogsAuditTrailtrait (wraps Spatie Activity Log) - Logs only dirty (changed) attributes β no noise from unchanged fields
- Ignored fields per model (e.g.,
last_login_at,failed_login_counton User) - Admin audit log endpoint with filtering support
- Failed login attempts logged separately per IP/user agent
- Account lockout after 3 failed login attempts (15-minute cooldown)
- Password history enforcement (last 5 passwords blocked)
- Rate limiting: 5 req/min on login, 10 req/min on sensitive operations, 60 req/min global API
- Tiered rate limiters configured in
AppServiceProvider - Cross-tenant isolation via Laravel route
scopeBindings() - Policy-enforced object-level authorization on every mutation
- Failed login attempt tracking (IP, user agent, timestamp)
- Login history with logout timestamps (per-session)
- Soft deletes across all major entities
- Token revocation on password change (all other sessions)
- Dashboard Stats: total rooms, occupied rooms, occupancy rate, room status breakdown, housekeeping stats, active maintenance count, active bookings count with 5-minute caching
- Revenue Stats: payments collected, total invoiced, room revenue, nights sold, ADR (Average Daily Rate), RevPAR (Revenue Per Available Room), payment method breakdown filterable by date range
- RESTful JSON API versioned under
/api/v1 - Swagger / OpenAPI 3.0 documentation (L5-Swagger) with PHP 8 attributes
- Spatie Query Builder integration for filtering, sorting, and pagination
- Consistent JSON response structure (
success,message,data) - Scoped route model binding for multi-tenant resource isolation
| Layer | Technology |
|---|---|
| Language | PHP 8.3+ |
| Framework | Laravel 13.x |
| Authentication | Laravel Sanctum 4.x |
| Authorization | Spatie Laravel Permission 8.x |
| Database | SQLite (development/testing), MySQL/PostgreSQL (production-ready) |
| Queue Driver | Database (configurable - Redis recommended for production) |
| API Documentation | L5-Swagger (OpenAPI 3.0) |
| Audit Logging | Spatie Laravel Activity Log 5.x |
| API Querying | Spatie Laravel Query Builder 7.x |
| PDF Generation | barryvdh/laravel-dompdf 3.x |
| Testing | PHPUnit 12.x |
| Code Style | Laravel Pint |
| IDE Support | Barryvdh Laravel IDE Helper |
| Debugging | Barryvdh Laravel Debugbar |
| Log Tailing | Laravel Pail |
| Concurrency (Dev) | npx concurrently |
| Package | Version | Purpose |
|---|---|---|
laravel/framework |
^13.8 | Core framework |
laravel/sanctum |
^4.3 | API token authentication |
laravel/tinker |
^3.0 | REPL for development |
spatie/laravel-permission |
^8.0 | Roles & permissions (RBAC) |
spatie/laravel-activitylog |
^5.0 | Audit logging |
spatie/laravel-query-builder |
^7.3 | Filtering, sorting & pagination |
barryvdh/laravel-dompdf |
^3.1 | Invoice and payment receipt PDF generation |
darkaonline/l5-swagger |
^11.0 | OpenAPI / Swagger documentation |
| Package | Version | Purpose |
|---|---|---|
phpunit/phpunit |
^12.5 | Unit and feature testing |
fakerphp/faker |
^1.23 | Test data generation |
barryvdh/laravel-debugbar |
^4.2 | Request profiling & debugging |
barryvdh/laravel-ide-helper |
^3.7 | IDE auto-completion support |
laravel/pint |
^1.27 | PHP code style fixer |
laravel/pail |
^1.2.5 | Real-time log tailing |
laravel/pao |
^1.0.6 | Development workflow |
mockery/mockery |
^1.6 | Mocking for tests |
nunomaduro/collision |
^8.6 | Beautiful error reporting |
Hotelia API follows a Service Layer architecture with clean separation of concerns:
HTTP Request β Middleware β Controller β Policy β Service β Model β Database
β
Event β Listener β Notification
| Layer | Purpose |
|---|---|
| Controllers | Thin - receive validated input, delegate to services, return JSON responses. Never contain business logic. |
| Services | Core business logic layer. Wrap operations in DB transactions. Dispatch events. |
| Policies | Object-level authorization. Enforce hotel-scoped ownership before mutations. |
| Requests (FormRequest) | Declarative input validation and authorization check, decoupled from controllers. |
| Resources | Transform Eloquent models into consistent, versioned JSON API representations. |
| Events | Domain-level state change signals (e.g., BookingCreated, HousekeepingTaskUpdated). |
| Listeners | React to events asynchronously - send notifications, trigger side effects. |
| Notifications | Structured in-app (database channel) messages delivered to relevant users. |
| Models | Eloquent entities - define relationships, casts, fillable fields, and boot hooks. |
| Factories | Realistic test data generation for all major entities. |
| Seeders | Deterministic database seeding: roles, permissions, super admin, and demo data. |
| Tests | Feature tests (full HTTP round-trips) and Unit tests (policy logic) with in-memory SQLite. |
| Traits | LogsAuditTrail - reusable behavior mixed into any Eloquent model. |
| Constants | Typed PHP classes/enums for statuses, permissions, roles, and notification types - no magic strings. |
| Jobs | Queued, schedulable background work (auto-cancel stale bookings, notify stuck rooms). |
hotelia-api/
βββ app/
β βββ Constants/ # Typed constants and enums
β β βββ BookingStatus.php # pending, confirmed, checked_in, checked_out, cancelled, no_show
β β βββ InvoiceStatus.php
β β βββ NotificationTypes.php
β β βββ PaymentStatus.php
β β βββ Permissions.php # All 45 permission strings
β β βββ Roles.php # Roles enum (super_admin, hotel_manager, etc.)
β β βββ RoomStatus.php # available, occupied, cleaning, maintenance, reserved
β β
β βββ Events/ # Domain events by module
β β βββ Billing/ # InvoiceGenerated, InvoicePaid
β β βββ Bookings/ # BookingCreated, BookingCancelled, BookingCheckedIn, BookingCheckedOut, BookingUpdated
β β βββ Guests/ # GuestCreated, GuestUpdated, GuestDeleted
β β βββ Hotels/ # HotelCreated, HotelUpdated, HotelDeleted, HotelSettingUpdated
β β βββ Housekeeping/ # HousekeepingTaskCreated, HousekeepingTaskUpdated, HousekeepingTaskDeleted
β β βββ Maintenance/ # MaintenanceRequestCreated, MaintenanceRequestUpdated, MaintenanceRequestDeleted
β β βββ Rooms/ # Room, RoomType, Amenity lifecycle events
β β βββ Services/ # ServiceCreated, ServiceUpdated, ServiceDeleted
β β
β βββ Http/
β β βββ Controllers/Api/V1/ # Versioned API controllers by domain
β β β βββ Admin/ # UserController (unlock), AuditController
β β β βββ Auth/ # AuthController, PasswordResetController
β β β βββ Billing/ # InvoiceController, PaymentController
β β β βββ Bookings/ # BookingController
β β β βββ Guests/ # GuestController
β β β βββ Hotels/ # HotelController, HotelSettingController
β β β βββ Housekeeping/ # HousekeepingController
β β β βββ Maintenance/ # MaintenanceController
β β β βββ Notifications/ # NotificationController
β β β βββ Pricing/ # RatePlanController, PricingRuleController
β β β βββ Reports/ # ReportController
β β β βββ Rooms/ # RoomController, RoomTypeController, AmenityController, AvailabilityController
β β β βββ Security/ # SecurityController (login history, failed logins)
β β β βββ Services/ # ServiceController
β β β βββ Users/ # UserController
β β β
β β βββ Requests/ # FormRequest classes for validation
β β β βββ Api/V1/ # Versioned request classes per domain
β β β βββ Auth/ # LoginRequest, ChangePasswordRequest, ForgotPasswordRequest, ResetPasswordRequest
β β β
β β βββ Resources/Api/V1/ # API resource transformers per domain
β β
β βββ Jobs/
β β βββ AutoCancelStaleBookings.php # Cancels pending bookings older than N minutes
β β βββ NotifyStuckCleaningRooms.php # Alerts staff about rooms stuck in cleaning
β β
β βββ Listeners/ # Event listeners grouped by domain
β β βββ Billing/ # SendInvoiceNotification
β β βββ Bookings/ # SendBookingNotification, SendGuestBookingMailNotification
β β βββ Hotels/ # LogHotelCreated, LogHotelUpdated, LogHotelDeleted, LogHotelSettingUpdated, NotifySuperAdmins*
β β βββ Housekeeping/ # SendHousekeepingTaskNotification
β β βββ Maintenance/ # SendMaintenanceRequestNotification
β β
β βββ Mail/ # Transactional mailable classes
β β βββ Billing/ # InvoiceGeneratedMail, InvoicePaidMail
β β βββ Bookings/ # BookingConfirmationMail, BookingCancelledMail, BookingUpdatedMail, BookingNoShowMail
β β
β βββ Models/ # Eloquent models
β β βββ Booking.php # With BookingStatusHistory boot hook
β β βββ BookingRoom.php # Pivot with price_per_night
β β βββ BookingService.php # Pivot with quantity and price
β β βββ BookingStatusHistory.php
β β βββ FailedLoginAttempt.php
β β βββ Guest.php
β β βββ Hotel.php
β β βββ HotelSetting.php
β β βββ HousekeepingTask.php
β β βββ Invoice.php
β β βββ InvoiceItem.php
β β βββ LoginHistory.php
β β βββ MaintenanceRequest.php
β β βββ PasswordHistory.php
β β βββ Payment.php
β β βββ PricingRule.php
β β βββ RatePlan.php
β β βββ Room.php
β β βββ RoomStatusHistory.php
β β βββ RoomType.php
β β βββ Service.php
β β βββ User.php
β β
β βββ Notifications/ # In-app notification classes
β β βββ BaseNotification.php
β β βββ Bookings/ # BookingNotification
β β βββ Hotels/ # NewHotelCreatedNotification, HotelUpdatedNotification
β β βββ Housekeeping/ # HousekeepingTaskNotification, StuckInCleaningNotification
β β βββ Maintenance/ # MaintenanceRequestNotification
β β
β βββ Policies/ # Laravel policies for object-level auth
β β βββ AmenityPolicy.php
β β βββ BookingPolicy.php
β β βββ GuestPolicy.php
β β βββ HotelPolicy.php
β β βββ HotelSettingPolicy.php
β β βββ HousekeepingTaskPolicy.php
β β βββ InvoicePolicy.php
β β βββ MaintenanceRequestPolicy.php
β β βββ PaymentPolicy.php
β β βββ PricingRulePolicy.php
β β βββ RatePlanPolicy.php
β β βββ RoomPolicy.php
β β βββ RoomTypePolicy.php
β β βββ ServicePolicy.php
β β βββ UserPolicy.php
β β
β βββ Providers/
β β βββ AppServiceProvider.php # Event bindings, rate limiter configuration
β β
β βββ Services/ # Business logic layer
β β βββ Billing/ # BillingService (invoices, payments, reconciliation)
β β βββ Booking/ # BookingService (create, update, cancel, check-in/out)
β β βββ Guest/ # GuestService
β β βββ Hotel/ # HotelService, HotelSettingService, AncillaryService
β β βββ Housekeeping/ # HousekeepingService (task lifecycle + room status sync)
β β βββ Maintenance/ # MaintenanceService
β β βββ Pdf/ # PdfService (invoice and receipt rendering)
β β βββ Pricing/ # PricingService (nightly rate calculation)
β β βββ Report/ # ReportService (dashboard stats, revenue KPIs)
β β βββ Room/ # RoomService, RoomTypeService, AmenityService, AvailabilityService
β β βββ User/ # UserService
β β
β βββ Traits/
β βββ LogsAuditTrail.php # Reusable Spatie activity log integration
β
βββ database/
β βββ factories/ # Eloquent model factories for all entities
β βββ migrations/ # 34 ordered migrations
β βββ seeders/
β βββ DatabaseSeeder.php
β βββ DemoDataSeeder.php
β βββ RolesAndPermissionsSeeder.php
β βββ SuperAdminSeeder.php
β
βββ routes/
β βββ api.php # Main API entry point (v1 prefix + global throttle)
β βββ api/ # Modular route files per domain
β βββ admin.php # Audit logs, user unlock
β βββ auth.php # Login, logout, password, token
β βββ billing.php # Invoices, payments
β βββ bookings.php # Booking CRUD + status transitions
β βββ guests.php
β βββ hotels.php # Hotels + hotel settings
β βββ housekeeping.php
β βββ maintenance.php
β βββ notifications.php
β βββ pricing.php # Rate plans & pricing rules
β βββ reports.php # Dashboard & revenue reports
β βββ rooms.php # Rooms, room types, amenities
β βββ security.php # Login history, failed logins
β βββ services.php
β βββ users.php
β
βββ tests/
βββ ApiTestCase.php # Shared API test case base
βββ Feature/Api/V1/ # Full HTTP integration tests per domain
βββ Feature/Jobs/ # Scheduled job tests
βββ Feature/Performance/ # Caching performance tests
βββ Traits/InteractsWithHotels.php
βββ Unit/Policies/ # Policy unit tests
βββ Unit/Services/ # Service unit tests (e.g. PricingService)
The database is designed around a multi-hotel tenancy model, where every resource is scoped to a specific hotel.
| Table | Description |
|---|---|
users |
Staff accounts with soft deletes, lockout fields, password timestamps |
hotels |
Hotel profiles (name, slug, location, logo, active flag) |
hotel_settings |
Per-hotel configuration (currency, timezone, tax rate, fees, prefixes) |
hotel_user |
Pivot - assigns staff to hotels (many-to-many) |
room_types |
Room categories with base pricing and capacity per hotel |
rooms |
Physical rooms linked to a room type with real-time status |
amenities |
Global amenities catalog |
room_type_amenity |
Pivot - amenity assignments per room type |
guests |
Guest profiles (name, contact, nationality, ID info) |
bookings |
Booking records with reference, dates, occupancy counts, total amount, status |
booking_rooms |
Pivot - rooms per booking with price snapshot |
booking_services |
Pivot - ancillary services per booking with quantity and price snapshot |
booking_status_histories |
Immutable audit trail of every booking status change |
services |
Ancillary services per hotel (e.g., airport transfer, spa) |
rate_plans |
Named rate plans with modifiers, policies, and default flags |
pricing_rules |
Date/day/stay-based price rules scoped to hotel, rate plan, or room type |
invoices |
Auto-generated invoices per booking with tax computation |
invoice_items |
Line items: room stays + service charges |
payments |
Payment transactions per booking (method, amount, status, reference) |
housekeeping_tasks |
Room cleaning tasks with status and completion timestamps |
maintenance_requests |
Room maintenance requests with priority and status |
room_status_histories |
Audit log of room status changes |
login_histories |
Per-session login/logout records (IP, user agent) |
failed_login_attempts |
Brute-force detection log |
password_histories |
Last-N password hashes for reuse prevention |
notifications |
Laravel database-channel notifications |
personal_access_tokens |
Sanctum API tokens |
activity_log |
Spatie audit trail |
Hotel ββ< RoomType ββ< Room ββ< HousekeepingTask
βββ< MaintenanceRequest
Hotel ββ< Booking ββ< BookingRoom >ββ Room
βββ< BookingService >ββ Service
βββ< Invoice ββ< InvoiceItem
βββ< Payment
βββ> RatePlan (optional)
Hotel ββ< RatePlan ββ< PricingRule
Hotel >ββ< User (via hotel_user pivot)
RoomType >ββ< Amenity (via room_type_amenity pivot)
- Every resource (rooms, bookings, services, housekeeping, maintenance) is scoped to a
hotel_id - Laravel's
scopeBindings()enforces hierarchical route model binding - a booking can only be resolved if it belongs to the hotel in the URL - Policies enforce that the authenticated user belongs to the target hotel before any mutation
- The
hotel_userpivot allows staff to be assigned to multiple hotels
A dedicated migration adds the following indexes for query performance:
bookings(hotel_id, status)- status-filtered booking queriesbookings(check_in_date, check_out_date)- date range availability checksbookings(hotel_id, status, check_in_date, check_out_date)- compound tenant availability indexhousekeeping_tasks(room_id, status)- room-specific task filteringmaintenance_requests(room_id, status)- room-specific maintenance filteringpayments(booking_id, status)- payment reconciliation queriesrooms(hotel_id, room_number)UNIQUE - prevents duplicate room numbers per hotel
/api/v1
The API is versioned at the URL level (/api/v1/). All controllers, requests, and resources are namespaced under Api/V1/ to allow parallel version development.
All protected routes require a Bearer token in the Authorization header:
Authorization: Bearer <sanctum-token>Tokens are issued on login and revoked on logout or password change.
| Module | Base Path | Actions |
|---|---|---|
| Authentication | /api/v1/auth |
login, logout, logout-all, me, status, refresh-token, change-password, forgot-password, reset-password |
| Hotels | /api/v1/hotels |
CRUD |
| Hotel Settings | /api/v1/hotels/{hotel}/settings |
show, update |
| Room Types | /api/v1/hotels/{hotel}/room-types |
CRUD |
| Rooms | /api/v1/hotels/{hotel}/rooms |
CRUD |
| Availability | /api/v1/hotels/{hotel}/availability |
index (date-range matrix with pricing) |
| Amenities | /api/v1/amenities |
index, store, update, destroy |
| Rate Plans | /api/v1/hotels/{hotel}/rate-plans |
CRUD |
| Pricing Rules | /api/v1/hotels/{hotel}/pricing-rules |
CRUD |
| Guests | /api/v1/hotels/{hotel}/guests |
CRUD |
| Services | /api/v1/hotels/{hotel}/services |
CRUD |
| Bookings | /api/v1/hotels/{hotel}/bookings |
CRUD + cancel, check-in, check-out |
| Invoices | /api/v1/hotels/{hotel}/bookings/{booking}/invoice |
show, regenerate, PDF download |
| Payments | /api/v1/hotels/{hotel}/bookings/{booking}/payments |
index, store, update status, receipt PDF |
| Housekeeping | /api/v1/hotels/{hotel}/housekeeping |
CRUD |
| Maintenance | /api/v1/hotels/{hotel}/maintenance |
CRUD |
| Notifications | /api/v1/notifications |
index, mark-as-read |
| Reports | /api/v1/hotels/{hotel}/reports |
dashboard, revenue |
| Security | /api/v1/login-history, /api/v1/failed-logins |
index |
| Admin | /api/v1/admin |
audit-logs, user unlock |
| Users | /api/v1/users |
CRUD |
All write operations use dedicated FormRequest classes with Laravel's built-in validation. Validation errors are returned as standard 422 responses.
Routes that support it use Spatie Laravel Query Builder - clients can pass filter[field]=value, sort=field or sort=-field (descending), and page[number]=1&page[size]=15 query parameters.
| HTTP Code | Meaning |
|---|---|
200 |
Success |
201 |
Resource created |
401 |
Unauthenticated |
403 |
Forbidden (policy denied) |
404 |
Resource not found |
422 |
Validation failed |
423 |
Account locked |
429 |
Too many requests (rate limited) |
500 |
Server error |
All responses follow a consistent envelope:
{
"success": true,
"message": "Bookings Retrieved Successfully.",
"data": { ... }
}| Feature | Implementation |
|---|---|
| API Authentication | Laravel Sanctum - stateless Bearer tokens |
| Role-Based Access Control | Spatie Permission - 5 roles, 45 permissions |
| Object-Level Authorization | Laravel Policies - hotel-scoped ownership enforced on every mutation |
| Cross-Tenant Isolation | scopeBindings() on all nested hotel routes |
| Account Lockout | Auto-lock after 3 failed attempts, 15-minute cooldown |
| Rate Limiting | 5/min (login), 10/min (sensitive ops), 60/min (global API) - per IP or user ID |
| Password History | Last 5 password hashes stored - reuse blocked |
| Session Revocation | All other tokens revoked on password change |
| Audit Logging | Spatie Activity Log on all major models - only dirty fields recorded |
| Failed Login Tracking | IP address, user agent, and timestamp logged per failed attempt |
| Login History | Per-session login/logout tracking |
| Input Validation | All inputs validated via FormRequest before hitting any service layer |
| Soft Deletes | All major entities use soft deletes - no permanent data loss |
Events are registered in AppServiceProvider::boot().
| Event | Listener | Purpose |
|---|---|---|
BookingCreated |
SendBookingNotification |
Notifies relevant hotel staff of new booking |
BookingCreated |
SendGuestBookingMailNotification |
Sends booking confirmation email to guest |
BookingUpdated |
SendBookingNotification |
Notifies staff of booking changes |
BookingUpdated |
SendGuestBookingMailNotification |
Sends update or no-show email to guest |
BookingCancelled |
SendBookingNotification |
Notifies staff of cancellation |
BookingCancelled |
SendGuestBookingMailNotification |
Sends cancellation email to guest |
BookingCheckedIn |
SendBookingNotification |
Notifies staff of guest check-in |
BookingCheckedOut |
SendBookingNotification |
Notifies staff of guest check-out |
InvoiceGenerated |
SendInvoiceNotification |
Sends invoice email to guest |
InvoicePaid |
SendInvoiceNotification |
Sends paid-invoice email to guest |
HousekeepingTaskCreated |
SendHousekeepingTaskNotification |
Notifies housekeeping team of new task |
HousekeepingTaskUpdated |
SendHousekeepingTaskNotification |
Notifies on task status changes |
HousekeepingTaskDeleted |
SendHousekeepingTaskNotification |
Notifies on task deletion |
MaintenanceRequestCreated |
SendMaintenanceRequestNotification |
Notifies maintenance staff of new request |
MaintenanceRequestUpdated |
SendMaintenanceRequestNotification |
Notifies on maintenance status changes |
MaintenanceRequestDeleted |
SendMaintenanceRequestNotification |
Notifies on maintenance request deletion |
Additional events exist for hotels, guests, rooms, room types, amenities, and services β these are dispatched for activity-log observability and future extensibility, but do not currently have active listeners beyond audit logging.
All in-app notifications use the database channel (stored in notifications table) and extend BaseNotification.
| Notification | Trigger | Recipients |
|---|---|---|
BookingNotification |
Booking created, cancelled, checked-in, checked-out | Hotel staff |
NewHotelCreatedNotification |
Hotel created | Super admins |
HotelUpdatedNotification |
Hotel updated | Super admins |
HousekeepingTaskNotification |
Housekeeping task created or updated | Housekeeping-permissioned staff |
StuckInCleaningNotification |
Room stuck in cleaning status for > N minutes |
Housekeeping-permissioned staff (with cooldown deduplication via cache) |
MaintenanceRequestNotification |
Maintenance request created | Maintenance-permissioned staff |
Transactional emails are sent synchronously via Laravel Mail when SMTP is configured. See Transactional Email under Features.
| Mailable | Trigger | Recipient |
|---|---|---|
BookingConfirmationMail |
Booking created | Guest |
BookingUpdatedMail |
Booking updated | Guest |
BookingCancelledMail |
Booking cancelled | Guest |
BookingNoShowMail |
Booking marked no-show | Guest |
InvoiceGeneratedMail |
Invoice generated | Guest |
InvoicePaidMail |
Invoice fully paid | Guest |
php artisan queue:listen --tries=1 --timeout=0Both jobs implement ShouldQueue with 3 retry attempts and exponential backoff (10s, 30s, 60s).
Defined in routes/console.php - both jobs run every 15 minutes:
| Job | Schedule | Purpose |
|---|---|---|
AutoCancelStaleBookings |
Every 15 minutes | Cancels pending bookings older than BOOKING_AUTO_CANCEL_MINUTES (default: 120 min) |
NotifyStuckCleaningRooms |
Every 15 minutes | Notifies staff of rooms stuck in cleaning status for > ROOM_CLEANING_ALERT_MINUTES (default: 120 min) - with cache-based deduplication to prevent spam |
Start the Laravel task scheduler (typically via cron):
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1- PHP 8.3+
- Composer 2.x
- Node.js 18+ (for Vite/npm scripts)
- MySQL / PostgreSQL (or SQLite for local development)
- A queue worker (database driver works out of the box)
1. Clone the repository
git clone https://github.com/your-org/hotelia-api.git
cd hotelia-api2. Install PHP dependencies
composer install3. Copy environment file
cp .env.example .env4. Generate application key
php artisan key:generate5. Configure database
Edit .env with your database credentials:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=hotelia
DB_USERNAME=root
DB_PASSWORD=secret6. Run database migrations
php artisan migrate7. Seed the database
# Seed roles, permissions, and super admin account
php artisan db:seed
# Optionally seed demo data
php artisan db:seed --class=DemoDataSeeder8. Create storage symlink
php artisan storage:link9. Generate Swagger documentation
php artisan l5-swagger:generate10. Start the development server
# Start all services concurrently (server + queue + logs + vite)
composer run devOr individually:
php artisan serve # API server
php artisan queue:listen # Queue worker
php artisan schedule:work # Task scheduler (dev only)| Variable | Default | Description |
|---|---|---|
APP_NAME |
hotelia-api |
Application name |
APP_ENV |
local |
Environment (local, production) |
APP_KEY |
- | Application encryption key (set via key:generate) |
APP_DEBUG |
true |
Enable debug mode |
APP_URL |
http://localhost |
Base application URL |
DB_CONNECTION |
sqlite |
Database driver |
DB_HOST |
127.0.0.1 |
Database host |
DB_PORT |
3306 |
Database port |
DB_DATABASE |
hotelia |
Database name |
DB_USERNAME |
root |
Database username |
DB_PASSWORD |
- | Database password |
QUEUE_CONNECTION |
database |
Queue driver (database, redis, sync) |
CACHE_STORE |
database |
Cache driver (database, redis, array) |
MAIL_MAILER |
smtp |
Mail driver for password reset emails |
MAIL_HOST |
- | SMTP host |
MAIL_PORT |
587 |
SMTP port |
MAIL_USERNAME |
- | SMTP username |
MAIL_PASSWORD |
- | SMTP password |
MAIL_FROM_ADDRESS |
- | Sender email address |
FRONTEND_URL |
http://localhost:3000 |
Frontend URL for password reset links |
BOOKING_AUTO_CANCEL_MINUTES |
120 |
Pending booking stale threshold (minutes) |
ROOM_CLEANING_ALERT_MINUTES |
120 |
Room stuck-in-cleaning threshold (minutes) |
L5_SWAGGER_GENERATE_ALWAYS |
false |
Auto-regenerate Swagger docs on each request |
Tests use an in-memory SQLite database and run isolated β no external services required.
composer run test
# or
php artisan testphp artisan test --testsuite=Unitphp artisan test --testsuite=Featurephp artisan test tests/Feature/Api/V1/Bookings/BookingTest.phpphp artisan test --filter test_can_create_bookingphp artisan test --coverage
# or with HTML report
php artisan test --coverage-html coverage/| Suite | Location | Coverage |
|---|---|---|
| Feature / Auth | tests/Feature/Api/V1/Auth/ |
Login, logout, password change, token refresh |
| Feature / Hotels | tests/Feature/Api/V1/Hotels/ |
Hotel CRUD, settings |
| Feature / Rooms | tests/Feature/Api/V1/Rooms/ |
Rooms, room types, amenities, availability |
| Feature / Bookings | tests/Feature/Api/V1/Bookings/ |
Full booking lifecycle, email notifications |
| Feature / Billing | tests/Feature/Api/V1/Billing/ |
Invoices, payments, PDF, email notifications |
| Feature / Pricing | tests/Feature/Api/V1/Pricing/ |
Rate plans, pricing rules |
| Feature / Guests | tests/Feature/Api/V1/Guests/ |
Guest management |
| Feature / Housekeeping | tests/Feature/Api/V1/Housekeeping/ |
Task lifecycle + checkout integration |
| Feature / Maintenance | tests/Feature/Api/V1/Maintenance/ |
Maintenance request CRUD |
| Feature / Notifications | tests/Feature/Api/V1/Notifications/ |
Notification delivery |
| Feature / Reports | tests/Feature/Api/V1/Reports/ |
Dashboard stats, revenue KPIs |
| Feature / Security | tests/Feature/Api/V1/Security/ |
Login history, failed logins, cross-tenant tests |
| Feature / Services | tests/Feature/Api/V1/Services/ |
Ancillary services |
| Feature / Users | tests/Feature/Api/V1/Users/ |
User management |
| Feature / Jobs | tests/Feature/Jobs/ |
AutoCancelStaleBookings, NotifyStuckCleaningRooms |
| Feature / Performance | tests/Feature/Performance/ |
Report caching behavior |
| Unit / Policies | tests/Unit/Policies/ |
Booking, Room, RoomType, Service, Housekeeping, Maintenance, HotelSetting, RatePlan, PricingRule policies |
| Unit / Services | tests/Unit/Services/ |
PricingService nightly rate calculation |
Hotelia API uses L5-Swagger (OpenAPI 3.0) with PHP 8 attribute-based annotations directly on controller methods.
php artisan l5-swagger:generateAfter starting the server:
http://localhost:8000/api/documentation
Set in .env to regenerate on every request:
L5_SWAGGER_GENERATE_ALWAYS=trueNote: Disable
L5_SWAGGER_GENERATE_ALWAYSin production for performance.
git checkout -b feature/your-feature-name
# develop, test, commit
git push origin feature/your-feature-name
# open pull requestFollow this order to maintain architectural consistency:
- Migration - define the database schema
- Model - relationships, casts, fillable,
LogsAuditTrailtrait - Factory - realistic fake data for tests
- Seeder - add to
DemoDataSeederif applicable - Events -
ResourceCreated,ResourceUpdated,ResourceDeleted - Service - business logic wrapped in
DB::transaction(), dispatch events - Requests -
StoreXRequest,UpdateXRequestwith validation rules - Policy - hotel-scoped
viewAny,view,create,update,delete - Controller - thin; delegate to service, authorize via policy, return resource
- Resource - JSON transformer
- Routes - add to
routes/api/module.php, register inroutes/api.php - Listener (if needed) - notifications, side effects
- Tests - feature test covering all endpoints + unit test for policy
./vendor/bin/pint # Fix code style
./vendor/bin/pint --test # Check without fixingphp artisan ide-helper:generate
php artisan ide-helper:models| Module | Description |
|---|---|
| Rate Plans | Named plans (BAR, corporate, group) with modifiers attached to bookings |
| Dynamic Pricing Engine | Date-range, day-of-week, and stay-length pricing rules with priority ordering |
| Room Availability Matrix | Date-range availability query with per-night pricing |
| Transactional Email | Guest booking and invoice emails via SMTP (requires mail configuration) |
| PDF Documents | Invoice and payment receipt PDF downloads |
| Module | Description |
|---|---|
| Admin UI | Web front desk dashboard for hotel staff |
| Channel Management | OTA integration (Booking.com, Expedia) via channel manager adapter |
| Online Payments | Payment gateway integration (Stripe, Flutterwave, M-Pesa Daraja API) |
| Guest Portal API | Self-service endpoints for guests to view bookings and invoices |
| Bulk Operations | Bulk check-in, bulk housekeeping task assignment |
| Webhook System | Outbound webhooks for third-party integrations |
| Multi-Currency Billing | Invoice generation with FX conversion beyond hotel-configured currency |
| Staff Scheduling | Shift management for housekeeping and maintenance staff |
| Queued Email Delivery | Move transactional mail to queued jobs for production reliability |
| CI/CD & Deployment | Docker, GitHub Actions, staging/production runbooks |
Contributions are welcome! Please follow these guidelines:
- Fork the repository and create your feature branch from
main - Write tests - all new features must include corresponding feature tests
- Follow the architecture - use the Service Layer pattern; keep controllers thin
- Use constants - never use magic strings for statuses, roles, or permissions
- Document your API - annotate new controller methods with OpenAPI attributes
- Run the test suite before opening a pull request:
composer run test - Format your code:
./vendor/bin/pint - Write descriptive commit messages following conventional commits format
- Tests pass (
composer run test) - Code style clean (
./vendor/bin/pint --test) - New endpoints annotated with OpenAPI attributes
- Migration included for schema changes
- Policy updated or created for new resource
- Seeder updated if new permissions were added
This project is licensed under the MIT License. See the LICENSE file for details.
Brian Mulindi Senior Software Engineer - Laravel & API Architecture
- π Nairobi, Kenya
- πΌ Specializing in commercial-grade Laravel backends, RESTful API design, and clean architecture
- π¨ Hotelia API - built with a focus on scalability, security, and maintainability
Built with β€οΈ using Laravel Β· Spatie Β· L5-Swagger Β· DomPDF