Multi-Tenant Applicant Tracking System (ATS)
A complete, production-ready ATS built with pure PHP and MySQL. Designed for scalability, security, and ease of use.
- Features
- Requirements
- Installation
- Configuration
- Database Setup
- Usage
- API Documentation
- Testing
- Deployment
- Architecture
- Security
- License
- ✅ Multi-Tenant Architecture - Complete tenant isolation with subscription management
- ✅ Job Management - Create, publish, and manage job postings
- ✅ Candidate Management - Track candidates with CV uploads and tagging
- ✅ Application Pipeline - Customizable recruitment stages with drag-and-drop
- ✅ Interview Scheduling - Schedule and manage interviews with multiple interviewers
- ✅ Evaluations & Feedback - Collect structured feedback from interviewers
- ✅ Email Notifications - Automated and manual email templates
- ✅ Public Career Pages - Branded career sites per tenant
- ✅ Advanced Search & Filtering - Find candidates and jobs quickly
- ✅ Activity Logging - Complete audit trail
- ✅ Custom MVC Framework - Lightweight, beginner-friendly architecture
- ✅ RESTful API - Integration with external job boards and systems
- ✅ Role-Based Access Control - 7 distinct user roles
- ✅ Subscription & Billing - Plan management with quota enforcement
- ✅ File Upload System - Secure CV/document management
- ✅ CSRF Protection - Security tokens on all forms
- ✅ Password Hashing - Industry-standard bcrypt
- ✅ Responsive Design - Mobile-friendly interface
- PHP: 7.0 or higher (tested up to PHP 8.3)
- MySQL: 5.7+ or MariaDB 10.2+
- Web Server: Apache 2.4+ or Nginx 1.10+
- PHP Extensions:
- PDO (pdo_mysql)
- mbstring
- openssl
- json
- fileinfo
- PHP 8.0+ for better performance
- MySQL 8.0+ for improved query optimization
- 2GB RAM minimum
- SSL certificate for production
git clone https://github.com/ahmedsaadawi13/splashrecruit.git
cd splashrecruit<VirtualHost *:80>
ServerName splashrecruit.local
DocumentRoot /path/to/splashrecruit/public
<Directory /path/to/splashrecruit/public>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>server {
listen 80;
server_name splashrecruit.local;
root /path/to/splashrecruit/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}chmod -R 755 /path/to/splashrecruit
chmod -R 775 storage/uploads
chmod -R 775 storage/logscp .env.example .env# Application
APP_NAME="SplashRecruit"
APP_ENV=production
APP_URL=https://yourdomain.com
APP_DEBUG=false
# Database
DB_HOST=localhost
DB_NAME=splashrecruit
DB_USER=your_db_user
DB_PASS=your_db_password
# Mail
MAIL_FROM_ADDRESS=noreply@yourdomain.com
MAIL_FROM_NAME="SplashRecruit"
# Session
SESSION_LIFETIME=7200
# Timezone
APP_TIMEZONE=UTCmysql -u root -pCREATE DATABASE splashrecruit CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'splashrecruit'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON splashrecruit.* TO 'splashrecruit'@'localhost';
FLUSH PRIVILEGES;
EXIT;mysql -u splashrecruit -p splashrecruit < database.sqlThis will create all tables and insert seed data including:
- 3 subscription plans (Starter, Professional, Enterprise)
- Platform admin user
- 2 demo tenants (TechCorp, InnovateLabs)
- Demo users, jobs, candidates, and applications
- Default email templates
- Default recruitment pipelines
After importing the database, you can log in with:
Platform Admin:
- Email:
admin@splashrecruit.com - Password:
password123(change immediately!)
TechCorp Tenant Admin:
- Email:
sarah@techcorp.com - Password:
password123
TechCorp Recruiter:
- Email:
mike@techcorp.com - Password:
password123
- platform_admin - System-wide access, tenant management
- tenant_admin - Full access within their tenant
- recruiter - Manage jobs, candidates, applications
- hiring_manager - Review candidates, provide feedback
- interviewer - Conduct interviews, submit evaluations
- hr_assistant - Support recruiters with screening
- viewer - Read-only access
Access tenant career pages at:
https://yourdomain.com/careers/{tenant-slug}
Example:
https://yourdomain.com/careers/techcorp-solutions
All API requests require an API key passed via header or query parameter:
# Header method
curl -H "X-API-KEY: your_api_key" https://yourdomain.com/api/jobs
# Query parameter method
curl https://yourdomain.com/api/jobs?api_key=your_api_keyGET /api/jobsParameters:
department(optional) - Filter by departmentlocation(optional) - Filter by locationtype(optional) - Filter by employment type
Response:
{
"success": true,
"data": [
{
"id": 1,
"title": "Senior Software Engineer",
"slug": "senior-software-engineer",
"reference_code": "TC-SSE-001",
"department": "Engineering",
"location_city": "San Francisco",
"location_country": "USA",
"employment_type": "full_time",
"description": "...",
"published_at": "2025-01-15 10:00:00",
"apply_url": "https://yourdomain.com/careers/techcorp/jobs/senior-software-engineer"
}
]
}POST /api/applicationsHeaders:
X-API-KEY: your_api_key
Content-Type: application/json
Body:
{
"job_reference": "TC-SSE-001",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone": "+1-555-1234",
"city": "New York",
"country": "USA",
"source": "job_board"
}Response:
{
"success": true,
"message": "Application submitted successfully.",
"data": {
"application_id": 123,
"candidate_id": 456
}
}GET /api/applications/{application_id}/statusResponse:
{
"success": true,
"data": {
"application_id": 123,
"status": "in_review",
"applied_at": "2025-01-20 14:30:00",
"last_updated": "2025-01-21 10:15:00"
}
}- Login with valid credentials
- Login with invalid credentials
- Logout functionality
- Session persistence
- User can only see their tenant's data
- Cannot access other tenant's resources via URL manipulation
- Platform admin can view all tenants
- Create new job
- Edit existing job
- Publish/unpublish job
- View job applications
- Delete job
- Add candidate manually
- Upload CV
- View candidate profile
- Add tags to candidate
- Move candidate between stages
- View stage history
- Filter by stage
- View published jobs
- Submit application with CV upload
- Receive acknowledgement email
- List jobs via API
- Create application via API
- Check application status via API
Create /tests/connection_test.php:
<?php
require_once __DIR__ . '/../config/app.php';
require_once __DIR__ . '/../app/core/Database.php';
try {
$db = Database::getInstance();
echo "✓ Database connection successful!\n";
// Test query
$db->query("SELECT COUNT(*) as count FROM tenants");
$result = $db->fetch();
echo "✓ Found {$result['count']} tenants in database\n";
echo "\nAll tests passed!\n";
} catch (Exception $e) {
echo "✗ Error: " . $e->getMessage() . "\n";
}Run:
php tests/connection_test.php-
Security:
- Change all default passwords
- Set
APP_DEBUG=falsein.env - Enable HTTPS/SSL
- Configure firewall rules
- Restrict database access
-
Performance:
- Enable OPcache in PHP
- Configure MySQL query cache
- Set up CDN for assets
- Enable gzip compression
-
Backups:
- Set up automated database backups
- Back up
/storage/uploadsdirectory - Test restore procedures
-
Monitoring:
- Set up error logging
- Monitor disk space (uploads folder)
- Track application performance
- Set up uptime monitoring
For sending pending email notifications:
# Send pending notifications every 5 minutes
*/5 * * * * php /path/to/splashrecruit/cron/send_notifications.phpCreate /cron/send_notifications.php:
<?php
require_once __DIR__ . '/../config/app.php';
require_once __DIR__ . '/../app/core/Database.php';
require_once __DIR__ . '/../app/models/Notification.php';
require_once __DIR__ . '/../app/helpers/Mailer.php';
$notificationModel = new Notification();
$pending = $notificationModel->getPending(50);
foreach ($pending as $notification) {
// Process and send
$notificationModel->markAsSent($notification['id']);
}app/
├── core/ # Framework core (Router, Controller, Model, etc.)
├── controllers/ # Request handlers
├── models/ # Database models
├── views/ # HTML templates
└── helpers/ # Utility classes
config/ # Configuration files
public/ # Web root
├── index.php # Front controller
└── assets/ # CSS, JS, images
storage/
├── uploads/ # User uploaded files
└── logs/ # Application logs
- All requests go to
public/index.php - Router matches URL to controller method
- Controller loads models and processes request
- Controller renders view or returns JSON
- Response sent to client
- Tenant Isolation: All main tables include
tenant_id - Foreign Keys: Enforced for data integrity
- Indexes: Optimized for common queries
- UTC Storage: All timestamps stored in UTC
- SQL Injection: Prepared statements via PDO
- XSS: Output escaping in views via
View::e() - CSRF: Token validation on all POST requests
- Password Security: bcrypt hashing
- File Upload Security:
- MIME type validation
- Extension whitelisting
- Size limits
- Non-executable storage location
- Session Security:
- Secure session handling
- Session regeneration on login
- Timeout enforcement
- Never commit
.envfiles - Regularly update PHP and MySQL
- Use HTTPS in production
- Implement rate limiting for API endpoints
- Regular security audits
- Keep file permissions strict
Potential features for future versions:
- Real-time notifications (WebSockets)
- Advanced reporting and analytics
- Resume parsing (AI/ML)
- Video interview integration
- Mobile apps (iOS/Android)
- Slack/Teams integration
- GDPR compliance tools
- Advanced search (Elasticsearch)
- Multi-language support (i18n)
- ✅ Clean, readable code with consistent naming
- ✅ Strong multi-tenant isolation
- ✅ Comprehensive CRUD operations
- ✅ Security-first approach
- ✅ Well-documented database schema
- ✅ Beginner-friendly structure
- Indexes: Add composite indexes for frequently joined columns
- Caching: Implement Redis/Memcached for session storage
- Full-Text Search: Use MySQL full-text or Elasticsearch for better candidate search
- Email Service: Integrate real SMTP service (SendGrid, Amazon SES)
- File Storage: Consider S3 for scalability
- Rate Limiting: Add API rate limiting to prevent abuse
- Unit Tests: Add PHPUnit tests for critical business logic
For issues, questions, or contributions:
- Issues: https://github.com/ahmedsaadawi13/splashrecruit/issues
- Email: ahmed.sha3ban13@gmail.com
- Documentation: https://docs.splashrecruit.com
This project is open-source software licensed under the MIT License.
Built with ❤️ for recruiters and developers
SplashRecruit - Making recruitment simple and powerful