Multi-tenant customer support platform with a configurable chat flow + ticket management system.
Multi-Tenant Customer Support & Ticketing Platform SwiftDesk is a collaborative Laravel project built by two developers to simulate a real customer-service and service-desk environment.
The system allows multiple companies to use the same application while keeping their users, support flows, customers, tickets, SLAs, and reports separated.
Customers interact with a configurable support flow. If the issue cannot be resolved through self-service, SwiftDesk creates a ticket that can be assigned, taken by an agent, escalated, monitored, and resolved.
SwiftDesk should be:
- Useful in a real SME environment
- Strong enough for a developer portfolio
- Realistic for two junior developers to build
- Simple enough to understand and maintain
- Built using proper team development workflow
This project should demonstrate:
- Laravel development
- Database design
- Multi-tenant architecture
- Dynamic workflow logic
- Ticket lifecycle management
- Role-based access
- SLA tracking
- Reporting
- Git collaboration
- Pull requests
- Code review
- Docker
- Linux deployment
The goal is not to build the biggest system possible.
The goal is:
Build a smaller real-world system properly.
Before adding a feature, ask:
Would a real company actually use this?
If not, do not add it yet.
Do not add technologies or features only because they look impressive.
- Laravel
- PHP
- MySQL
- Laravel Blade
- Bootstrap 5
- Vanilla JavaScript
- Docker
- Docker Compose
- Apache
- Linux server
- Git
- GitHub
- GitHub Issues
- Pull Requests
SwiftDesk V1 does not require:
- React
- Vue
- Microservices
- Kubernetes
- Redis
- AI chatbot
- WhatsApp integration
- Mobile application
- Drag-and-drop flow builder
- Subscription billing
- Complex real-time architecture
These may be considered later.
SwiftDesk must support separate environments.
Developer A
Local Docker
Laravel
MySQL
|
+-- Local Database
Developer B
Local Docker
Laravel
MySQL
|
+-- Local Database
Server
Docker
Laravel
MySQL
|
+-- Staging / Demo Database
Each developer works using their own local database.
Developers should NOT use the server database for normal development.
Keep Docker simple.
Initial containers:
SwiftDesk
+-----------------------+
| Laravel / Apache |
| PHP |
+-----------+-----------+
|
v
+-----------------------+
| MySQL |
+-----------------------+
V1 starts with only:
- Application container
- MySQL container
Additional containers should only be added when genuinely required.
Each environment has its own:
.env
The real .env must never be committed.
GitHub should contain:
.env.example
Example:
APP_NAME=SwiftDesk
APP_ENV=local
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=swiftdesk
DB_USERNAME=swiftdesk
DB_PASSWORD=Do not place real passwords or API keys inside .env.example.
Repository name:
swiftdesk
Main branches:
main
develop
Purpose:
main
|
+-- Stable / production-ready code
develop
|
+-- Active shared development
Feature development must branch from develop.
Example:
develop
|
+-- feature/authentication
+-- feature/tenant-management
+-- feature/chat-flow
+-- feature/ticket-queue
Do not start building ticketing immediately.
The first milestone is only infrastructure.
Success means:
GitHub Repository
|
v
Laravel Project
|
v
Docker Compose
|
v
Laravel Container
|
v
MySQL Container
|
v
Database Connection Works
Both developers must be able to run SwiftDesk locally.
Eventually a new developer should be able to do something close to:
git clone <repository>
cd swiftdesk
cp .env.example .env
docker compose up -dThen run the required Laravel initialization commands.
Anything required to run SwiftDesk should come from the repository except:
- Passwords
- Secrets
- Production configuration
- Database data
Avoid undocumented manual server configuration.
The shared server is used for:
- Staging
- Demo
- Integration testing
Example structure:
Server
/srv/swiftdesk
|
+-- Laravel source
+-- compose.yaml
+-- .env
+-- Docker configuration
General flow:
GitHub
|
v
Server
|
v
docker compose up -d
|
v
SwiftDesk Staging / Demo
Build only:
- GitHub repository
- Laravel skeleton
- Docker environment
- MySQL
.env.example- Apache configuration
- Basic Bootstrap layout
- Basic authentication
maindevelop
Deliverable:
Developer A can run SwiftDesk locally.
Developer B can run SwiftDesk locally.
Server can run SwiftDesk.
Laravel can connect to MySQL.
Only after Phase 0 is stable should feature development begin.
High-level customer flow:
Customer
|
v
Company Support Page
|
v
Dynamic Support Flow
|
+------ Issue Solved
| |
| v
| Close Session
|
+------ Issue Not Solved
|
v
Create Ticket
|
v
Queue
|
+------+------+
| |
Agent Take Leader Assign
| |
+------+------+
|
v
L1
|
+-------+-------+
| |
Resolve Escalate
|
v
L2
|
Escalate
|
v
L3
|
Resolve
One SwiftDesk installation can support multiple companies.
Example:
SwiftDesk
+-- ABC Technology
| +-- Computer
| +-- Printer
| +-- Network
|
+-- XYZ Facilities
+-- Air Conditioning
+-- Electrical
+-- Plumbing
Each company has separate:
- Users
- Customers
- Support flows
- Tickets
- Categories
- SLA rules
- Reports
- Settings
Company A must never access Company B data.
Most business tables will contain:
tenant_id
Example:
tickets
id
tenant_id
ticket_number
customer_id
status
assigned_to
created_at
Tenant isolation must always be validated on the server.
Never trust tenant_id sent directly from the browser.
Responsible for SwiftDesk itself.
Can:
- Create company
- Disable company
- View company list
Can:
- Manage users
- Configure support flows
- Configure categories
- Configure SLA
- View reports
Can:
- View queue
- Assign tickets
- Reassign tickets
- Monitor team
- Escalate tickets
Agents may operate at:
L1
L2
L3
Agents can:
- Take ticket
- Update ticket
- Add note
- Resolve ticket
- Escalate ticket
Do not create separate applications for L1, L2, and L3.
Use the same Agent interface with different permissions.
Customer login is not required for V1.
Customer provides basic details such as:
- Name
- Mobile number
Then starts a support session.
Example URL:
/swiftdesk/support/abc-technology
or:
/support/abc-technology
Support flow must not be hardcoded.
Bad:
if ($answer == 1) {
echo "Computer";
}Correct concept:
Flow
|
Node
|
Option
|
Next Node
Example:
What problem are you having?
1. Computer
2. Printer
3. Network
Customer selects:
Computer
Next node:
What computer issue are you having?
1. Cannot turn on
2. Slow
3. Software issue
Possible option actions:
NEXT_NODE
CREATE_TICKET
CLOSE_SESSION
Keep the flow engine simple.
V1 does not require visual drag-and-drop.
Use normal admin forms.
Example:
Flow Name
[ IT Support ]
Node Name
[ Computer Problem ]
Message
[ What problem are you having? ]
Response Type
[ Multiple Choice ]
Options
[ Slow ]
Next Node: [ Computer Slow ]
[ Cannot Boot ]
Next Node: [ Boot Troubleshooting ]
[ Software Issue ]
Next Node: [ Software Support ]
Visual flow builder can come later.
Recommended statuses:
OPEN
ASSIGNED
IN_PROGRESS
PENDING
ESCALATED
RESOLVED
CLOSED
Support level should be stored separately.
Example:
Status: IN_PROGRESS
Support Level: L2
Readable format:
TKT-20260807-0001
The ticket number is not the primary key.
Database still uses:
id
Keep the database understandable.
Initial planned tables:
tenants
users
customers
flows
flow_nodes
flow_options
chat_sessions
chat_messages
ticket_categories
tickets
ticket_events
ticket_notes
sla_rules
Tables should only be created when their phase begins.
Do not create the entire database on Day 1.
id
name
slug
status
created_at
updated_at
id
tenant_id
name
email
password
role
support_level
status
created_at
updated_at
id
tenant_id
name
email
mobile
created_at
updated_at
id
tenant_id
name
status
created_at
updated_at
id
tenant_id
flow_id
name
message
node_type
created_at
updated_at
Node type:
QUESTION
MESSAGE
END
id
tenant_id
node_id
label
next_node_id
action
created_at
updated_at
Actions:
NEXT_NODE
CREATE_TICKET
CLOSE_SESSION
id
tenant_id
customer_id
flow_id
current_node_id
status
started_at
ended_at
id
tenant_id
chat_session_id
sender_type
message
created_at
Sender:
CUSTOMER
SYSTEM
AGENT
id
tenant_id
name
parent_id
status
created_at
updated_at
Example:
Computer
|
+-- Hardware
+-- Software
id
tenant_id
ticket_number
customer_id
chat_session_id
category_id
subject
description
priority
status
support_level
assigned_to
created_at
first_response_at
resolved_at
closed_at
Priority:
LOW
MEDIUM
HIGH
URGENT
id
tenant_id
ticket_id
user_id
event_type
old_value
new_value
notes
created_at
Important actions should create an event.
Example:
10:01 Ticket created
10:03 Assigned to Amir
10:10 OPEN -> IN_PROGRESS
10:40 Escalated L1 -> L2
11:20 Resolved
id
tenant_id
ticket_id
user_id
note
is_internal
created_at
updated_at
Add during the SLA phase.
id
tenant_id
priority
first_response_minutes
resolution_minutes
created_at
updated_at
Recommended events:
TICKET_CREATED
TICKET_ASSIGNED
TICKET_TAKEN
STATUS_CHANGED
PRIORITY_CHANGED
ESCALATED
REASSIGNED
NOTE_ADDED
RESOLVED
REOPENED
CLOSED
Ticket events provide:
- Ticket timeline
- Audit trail
- Reporting data
- Resolution calculation
/support/{company}
Example:
ABC Technology
How can we help you?
[ Start Support ]
/support/{company}/chat/{session}
Simple interface.
Example:
---------------------------------
ABC Technology Support
What problem are you having?
[ Computer ]
[ Printer ]
[ Network ]
---------------------------------
Do not copy WhatsApp exactly.
Example:
--------------------------------------
SwiftDesk | ABC Technology
Open Assigned Escalated
32 14 5
Resolved Today
18
Average Resolution
1h 42m
SLA Breach
3
--------------------------------------
Recent Tickets
TKT-00123 Computer Slow OPEN
TKT-00124 Printer Offline L2
TKT-00125 WiFi Issue ASSIGNED
Columns:
Ticket
Customer
Category
Priority
Level
Created
Waiting Time
Action
Agent action:
[ Take Ticket ]
Leader action:
[ Assign ]
Agents should have:
Tickets > My Tickets
Shows tickets currently assigned to the logged-in agent.
Example:
TKT-20260807-0012
Status:
IN_PROGRESS
Priority:
MEDIUM
Level:
L1
Customer:
John
Category:
Computer > Performance
Assigned:
Afizi
Sections:
Customer Conversation
Internal Notes
Ticket Timeline
Actions:
[ Add Note ]
[ Escalate ]
[ Resolve ]
Leader may additionally:
[ Reassign ]
Company Admin:
Settings
|
+-- Support Flows
Initial pages:
Flow List
Create Flow
Edit Flow
Flow Nodes
Create Node
Edit Node
Node Options
Keep the GUI simple first.
Example:
Users
Name
Email
Role
Level
Status
[ Add User ]
Possible users:
Company Admin
Team Leader
Agent L1
Agent L2
Agent L3
SwiftDesk V1 does not require a full public REST API.
Laravel web routes are enough.
Chat may use JSON endpoints where useful.
Example:
GET /support/{tenant}
POST /support/{tenant}/session
GET /support/{tenant}/session/{session}
POST /support/{tenant}/session/{session}/answer
Ticket routes:
GET /tickets
GET /tickets/{ticket}
POST /tickets/{ticket}/take
POST /tickets/{ticket}/assign
POST /tickets/{ticket}/status
POST /tickets/{ticket}/escalate
POST /tickets/{ticket}/resolve
POST /tickets/{ticket}/notes
Admin:
GET /admin/flows
POST /admin/flows
GET /admin/flows/{flow}
PUT /admin/flows/{flow}
POST /admin/flows/{flow}/nodes
PUT /admin/nodes/{node}
POST /admin/nodes/{node}/options
Do not build APIs simply to say the project has an API.
Build:
Repository
Laravel
Docker
MySQL
.env.example
Authentication
Bootstrap layout
Local environment
Server environment
Deliverable:
Both developers can run SwiftDesk locally.
Server deployment works.
Build:
- Tenant
- Users
- Roles
- Company Admin
- Tenant isolation
- Company settings
Create:
Company A
Company B
Critical test:
Company A cannot access Company B data.
Build:
- Flows
- Nodes
- Options
- Customers
- Chat sessions
- Chat messages
- Next-node processing
Deliverable:
Company A and Company B can configure completely different support flows.
Build:
- Ticket creation from chat
- Ticket number
- Ticket queue
- Take ticket
- Assign ticket
- My Tickets
- Ticket details
- Internal notes
- Ticket history
- Resolve ticket
Deliverable:
Customer
|
v
Chat
|
v
Ticket
|
v
Queue
|
v
Agent
|
v
Resolve
Build:
L1 -> L2 -> L3
Track:
- Escalation reason
- Previous level
- New level
- User
- Date/time
Build:
- SLA rules
- First response time
- Resolution time
- SLA deadline
- SLA breach indicator
Example:
Priority:
HIGH
Created:
10:00 AM
Resolution SLA:
4 hours
Deadline:
2:00 PM
Use backend/server-side calculations.
Add only after the ticket system works.
Useful metrics:
Total Tickets
Open Tickets
Resolved Tickets
Average Resolution Time
Average First Response Time
Tickets by Category
Tickets by Agent
Tickets by Priority
Escalation Rate
SLA Breach Rate
Self-Service Resolution Rate
Avoid unnecessary charts.
Deploy:
Docker
HTTPS
Production configuration
Database persistence
Logs
Backup
Demo tenants
Demo users
Demo ticket data
Never use real company or customer data.
SwiftDesk belongs to both developers.
Both developers should contribute meaningful code.
Do not divide the project like:
Developer A
Everything backend
Developer B
Bootstrap only
Both developers should experience:
- Backend
- Database
- Frontend
- Debugging
- Git
- Pull requests
- Code review
Never develop directly on:
main
Normal flow:
develop
|
v
feature/xxx
|
v
Pull Request
|
v
Review
|
v
develop
Production:
develop
|
v
Test
|
v
Pull Request
|
v
main
Features:
feature/authentication
feature/tenant-management
feature/chat-engine
feature/ticket-queue
feature/sla
Fix:
fix/ticket-assignment
fix/tenant-access
Refactor:
refactor/ticket-service
Documentation:
docs/database-design
Every meaningful feature should have a PR.
Include:
What changed?
Why?
How to test?
Database changes?
Screenshots?
Example:
Feature:
Agent can take an available ticket.
Changes:
- Added ticket assignment logic
- Added assigned_to
- Added ticket event
- Updated queue interface
Testing:
1. Login as Agent
2. Open Queue
3. Click Take
4. Ticket should appear in My Tickets
Whenever possible:
Developer A writes
Developer B reviews
Next feature:
Developer B writes
Developer A reviews
Reviewer should actually read the code before approving.
All database structure changes must use Laravel migrations.
Correct:
php artisan make:migration
Do not manually change production structure and forget to record the change.
Database schema must be reproducible from Git.
Keep code understandable.
Use Laravel conventions first.
When logic becomes complicated, services such as these may be introduced:
FlowService
TicketService
SlaService
Do not create unnecessary layers simply because they look advanced.
Rule:
Extract when complexity actually appears.
All important input must be validated server-side.
Examples:
- Mobile
- Ticket priority
- Ticket status
- Support level
- Flow selection
- User assignment
- Tenant access
Frontend validation improves user experience.
Backend validation protects the application.
SwiftDesk must implement:
- Authentication
- Authorization
- Tenant isolation
- CSRF protection
- Laravel validation
- Password hashing
- Environment variables
- No secrets in Git
- No real customer information in demo data
Never trust:
tenant_id
user_id
role
support_level
coming directly from the browser.
Application timezone:
Asia/Kuala_Lumpur
Use consistent date/time display.
Example:
07 Aug 2026
11:30 AM
If currency is introduced later:
RM
MYR
Keep phone validation simple during early phases.
Create at least two.
Support flow:
Computer
Printer
Network
Support flow:
Air Conditioning
Electrical
Plumbing
This provides a clear multi-tenant demonstration.
A feature is not done because:
"It works on my laptop."
A feature is done when:
- Code works
- Validation exists
- Authorization is checked
- Tenant isolation is checked
- Migration exists when required
- Basic testing completed
- No obvious errors
- PR opened
- Other developer reviewed
- Merged into
develop
Do not chase 100% automated coverage.
Focus on important business rules.
Examples:
Tenant A cannot access Tenant B ticket.
Two agents should not accidentally take the same available ticket.
L1 cannot perform L3-only actions.
Resolved ticket records resolved_at.
Escalation creates ticket event.
Dynamic flow moves to the correct next node.
CREATE_TICKET creates the correct ticket.
Do not add these until the core application is complete:
AI
WhatsApp
Telegram
Mobile App
React
Microservices
Kubernetes
Payment Gateway
Subscriptions
Drag-and-Drop Flow Builder
Complex Real-Time Chat
Machine Learning
After V1 is stable:
Email notifications
Customer ticket tracking
Live agent messaging
Saved replies
Knowledge base
Improved SLA rules
Team management
Flow versioning
External webhooks
Later:
AI ticket summarization
AI suggested response
Intent classification
Automatic category suggestion
WhatsApp integration
Visual flow builder
Public API
Subscription plans
Both developers should list:
SwiftDesk
Collaborative Project
Clearly state:
Developed collaboratively with another developer.
Each developer should document their own contribution.
Example:
My Contributions
- Multi-tenant architecture
- Dynamic support flow
- Customer chat
- Docker deployment
Other developer:
My Contributions
- Ticket workflow
- Agent queue
- Escalation
- SLA reporting
Shared contributions:
Database design
Architecture decisions
Testing
Code review
Deployment
A recruiter should eventually be able to:
1. Open ABC Technology support page
2. Select Computer
3. Select Slow Computer
4. Complete troubleshooting
5. Select Still Not Resolved
6. Receive ticket number
7. Login as Team Leader
8. See ticket in queue
9. Assign ticket
10. Login as Agent
11. View customer conversation
12. Escalate L1 -> L2
13. Resolve ticket
14. Dashboard updates
If this workflow works smoothly, SwiftDesk already demonstrates real business value.
SwiftDesk V1 is successful when:
✓ Both developers can run the same Docker environment
✓ Server deployment works
✓ Two tenants exist
✓ Tenant data is isolated
✓ Admin can configure support flows
✓ Customer can complete dynamic support flow
✓ Flow can generate ticket
✓ Ticket enters queue
✓ Agent can take ticket
✓ Leader can assign ticket
✓ Ticket can escalate L1 -> L2 -> L3
✓ Ticket can be resolved
✓ Full ticket timeline exists
✓ Resolution duration is measured
✓ SLA is tracked
✓ Basic reporting exists
✓ Production demo is online
✓ Both developers contributed through PRs
Everything beyond this is bonus.
Build in this order:
Environment works
|
v
Core workflow works
|
v
Business rules are correct
|
v
Security is correct
|
v
Reporting works
|
v
UI is improved
|
v
Advanced features
Never do this:
Fancy dashboard
|
v
AI
|
v
Animations
|
v
Beautiful login page
|
v
Ticket creation still broken
SwiftDesk should feel like software that a small Malaysian company could realistically use while remaining achievable for two junior developers learning and building together.