A production-ready, extensible calendar synchronization platform that acts as a universal bridge between any calendar systems. Built with PHP/Slim4, this system can synchronize events between Outlook (Microsoft 365) and any other calendar system using REST APIs.
OutlookBookingSync has been transformed into a Generic Calendar Bridge - a flexible, extensible platform that can connect any calendar system to any other calendar system. While it started as an Outlook-specific solution, it now supports universal calendar synchronization.
- 🌐 Bridge Pattern Architecture - Extensible to any calendar system
- 🔗 REST API Communication - Standard HTTP interfaces for all integrations
- 🏠 Self-Hosted Solution - Full control and customization
- 🏢 Production Ready - Enterprise-grade reliability and monitoring
- 👨💻 Developer Friendly - Easy to extend with new calendar adapters
- ✅ Universal Bridge System - Connect any calendar to any other calendar
- ✅ Complete Bidirectional Sync - Events flow seamlessly between systems
- ✅ Comprehensive Sync Status Tracking - Real-time monitoring, error recovery, and retry mechanisms
- ✅ Automatic Deletion Handling - Detects and syncs deletions across systems
- ✅ Resource Mapping Management - Map booking resources to calendar systems
- ✅ Webhook-Free Operation - Works perfectly with polling (no public IP needed)
- ✅ Real-time Webhooks - Optional instant sync for internet-accessible systems
- ✅ RESTful API - Comprehensive endpoints for all operations including sync status management
- ✅ Health Monitoring - Statistics, logs, and real-time sync status monitoring
- ✅ Interactive Dashboard - Web-based monitoring interface with sync management controls
- ✅ Docker Containerized - Easy deployment and scaling
┌─────────────────┐ REST API ┌─────────────────┐ REST API ┌─────────────────┐
│ │◄──────────────►│ │◄──────────────►│ │
│ Booking System │ │ Calendar Bridge │ │ Microsoft Graph │
│ │ │ (Middleware) │ │ API │
└─────────────────┘ └─────────────────┘ └─────────────────┘
- ✅ Microsoft Outlook/Graph API (Full implementation)
- ✅ Generic Booking Systems (REST API)
- 🔄 Google Calendar (Extensible - implement GoogleBridge)
- 🔄 CalDAV Systems (Extensible - implement CalDAVBridge)
- 🔄 Any Custom System (Implement AbstractCalendarBridge)
- PHP 8.4+
- PostgreSQL Database
- Docker & Docker Compose (recommended)
- Microsoft Graph API Credentials (for Outlook integration)
- Network access to target calendar systems
git clone <repository-url>
cd OutlookBookingSynccp .env.example .env
cp .env.compose.example .env.compose
# Edit .env with your database and Microsoft Graph credentials# Create bridge database schema
scripts/setup_bridge_database.shor just apply the database script directly in your PostgreSQL client.
database/bridge_schema.sql
# Using Docker (recommended)
docker compose up -d
# Or run directly with PHP
php -S localhost:8082 index.php# Check bridge health
curl -H "api_key: change-me-strong-random" http://localhost:8082/bridges/health
# List available bridges
curl -H "api_key: change-me-strong-random" http://localhost:8082/bridges
# Test resource discovery (example with outlook bridge)
curl -H "api_key: change-me-strong-random" http://localhost:8082/bridges/outlook/available-resources
curl -H "api_key: change-me-strong-random" http://localhost:8082/bridges/outlook/available-groups
# Test resource mapping API
curl -H "api_key: change-me-strong-random" http://localhost:8082/mappings/resourcesSee README_BRIDGE.md for detailed booking system API requirements.
- Future plan: see ROADMAP.md
- Development guide: doc/DEVELOPMENT.md
- Maintenance runbook: doc/MAINTENANCE.md
- Set an API key via
.env(development) or.env.compose(Docker). The application checks$_ENV['API_KEY']. - The dashboard prompts for the API key on first load and stores it in your browser. Press Ctrl+K to update it.
- For curl or scripts, send the header:
api_key: <your key>.
- Endpoint:
POST /maintenance/cleanup-logs?days=30removes old rows frombridge_sync_logs(defaults to 30 days if omitted). - Example:
curl -s -X POST "http://localhost:8082/maintenance/cleanup-logs?days=30" \
-H "api_key: change-me-strong-random"- Cron: The container runs this daily at 03:00. Override retention via
CLEANUP_DAYSin.env.compose.
Use this checklist before exposing the service in production.
Security
- Set a strong, unique
API_KEY(store in.env.composeor a secret manager). Rotate periodically. - Disable legacy endpoints: set
ENABLE_LEGACY_WEBHOOKS=false. - Terminate TLS at a reverse proxy (nginx/Traefik) and prefer private network exposure.
- Add proxy protections: rate limiting, request size limits, and optional IP allowlist for admin endpoints and
/dashboard.
Operations and resilience
- Run with Docker restart policy and a container healthcheck.
- Ensure PHP runs with production settings (display_errors off; error logging on).
- Verify cron schedules do not overlap and timezone is correct; keep
API_KEYavailable to cron (entrypoint already wires the header).
Observability
- Centralize logs (Apache/PHP/app) and alert on
/health/systemdegradation. - Track cron success/failure and set up basic metrics dashboards.
Data and database
- Apply migrations on deploy; set up automated backups and retention.
- Validate DB performance and connection limits under expected load.
CI/CD quality gates
- Add a minimal pipeline:
php -l, static analysis (PHPStan), and a few unit/integration tests.
Optional docker-compose hardening
services:
portico_outlook:
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 3# Example: Handle booking system cancellation (sets event to inactive)
# The bridge system will automatically detect and sync the deletion to Outlook
# 1. Set booking system event to inactive (via your booking system)
curl -X PUT http://your-booking-system/api/events/123 \
-d '{"status": "inactive"}'
# 2. Run deletion detection to sync to Outlook
curl -X POST -H "api_key: change-me-strong-random" http://localhost:8082/bridges/sync-deletions
# Example: Handle Outlook deletion
# When an Outlook event is deleted, webhooks or polling will detect it
# and automatically mark the corresponding booking system event as inactiveUniversal event identification across different calendar systems:
- Format:
{type}_{original_id}(e.g.,event_78269,booking_123) - Bidirectional Support: Works seamlessly in both sync directions
- Type Safety: Preserves original event type and ID for accurate API calls
- Universal Mapping: Enables correct addressing across any calendar system
Supported Event Types:
event_- Standard calendar events (Priority: 1 - Highest)booking_- Booking system reservations (Priority: 2)allocation_- Resource allocation entries (Priority: 3 - Lowest)meeting_- Meeting room bookings (Priority: 2)appointment_- Appointment entries (Priority: 2)
Intelligent conflict resolution for overlapping reservations:
- Automatic Priority Resolution: Handles multiple events in the same time slot
- Configurable Hierarchy: Event > Booking > Allocation priority levels
- Conflict Logging: Detailed audit trail of all filtering decisions
- Performance Optimized: Minimal overhead with efficient filtering algorithms
Enterprise-grade authentication for booking system integrations:
- Login Flow: Secure session establishment with username/password
- Session Management: Automatic token refresh and session maintenance
- API Security: Session tokens used for all API communications
- Fallback Support: Graceful handling of session expiration
# Discover available calendar resources
curl -X GET "http://localhost:8082/bridges/outlook/available-resources?query=conference&limit=10"
# Get calendar groups
curl -X GET "http://localhost:8082/bridges/outlook/available-groups?query=meeting&limit=5"# Sync events between systems (automatic composite ID handling)
curl -X POST "http://localhost:8082/bridges/sync/booking_system/outlook" \
-H "Content-Type: application/json" \
-d '{
"source_calendar_id": "room_123",
"target_calendar_id": "conference-room-a@company.com",
"apply_priority_filter": true
}'
# Response includes composite ID information
{
"success": true,
"synced_events": [
{
"composite_id": "event_78269",
"priority": 1,
"status": "synced"
}
],
"filtered_events": [
{
"composite_id": "booking_123",
"priority": 2,
"status": "filtered_due_to_priority"
}
]
}# Create resource mapping
curl -X POST "http://localhost:8082/mappings/resources" \
-H "Content-Type: application/json" \
-d '{
"bridge_from": "booking_system",
"bridge_to": "outlook",
"resource_id": "room_123",
"calendar_id": "conference-room-a@company.com"
}'
# Delete mapping by composite key
curl -X DELETE "http://localhost:8082/mappings/resources/by-key/booking_system/room_123/conference-room-a@company.com"DB_HOST,DB_PORT,DB_NAME,DB_USER,DB_PASS- Database configurationOUTLOOK_CLIENT_ID,OUTLOOK_CLIENT_SECRET,OUTLOOK_TENANT_ID,OUTLOOK_GROUP_ID- Microsoft Graph APIAPP_BASE_URL- Base URL for this service (used in links/webhooks)API_KEY- API key for endpoint security (send as headerapi_key)CLEANUP_DAYS- Days to keep sync logs (used by daily cleanup cron)RENEW_MINUTES- Renewal threshold in minutes for webhook subscriptions (hourly cron)
Bridges are automatically registered on service startup using environment variables. Configure your credentials in the .env file:
# Microsoft Graph API
OUTLOOK_CLIENT_ID=your_client_id
OUTLOOK_CLIENT_SECRET=your_client_secret
OUTLOOK_TENANT_ID=your_tenant_id
OUTLOOK_GROUP_ID=your_group_id
# Booking System API (Session-based authentication)
BOOKING_SYSTEM_API_URL=http://your-booking-system/api
BOOKING_SYSTEM_LOGIN=your_username
BOOKING_SYSTEM_PASSWORD=your_password
BOOKING_SYSTEM_DOMAIN=your_domain
BOOKING_SYSTEM_THROW_ON_FAILURE=true
# Application
APP_BASE_URL=https://bridge.example.com
API_KEY=replace_meThe bridges will be automatically available once the service starts.
GET /bridges- List all available bridgesGET /bridges/{bridge}/calendars- Get calendars for a bridgeGET /bridges/{bridge}/available-resources- Get available resources (rooms/equipment) for a bridgeGET /bridges/{bridge}/available-groups- Get available groups/collections for a bridgeGET /bridges/{bridge}/resources/{resourceId}/calendar-items- Get calendar items for a specific resource on a bridgePOST /bridges/sync/{from}/{to}- Sync events between bridgesPOST /bridges/webhook/{bridge}- Handle bridge webhooks (legacy/webhook/outlook-notificationsis redirected)POST /bridges/{bridge}/subscriptions- Create webhook subscriptions for a bridgeGET /bridges/health- Get health status of all bridges
GET /mappings/resources- Get all resource mappingsPOST /mappings/resources- Create new resource mappingPUT /mappings/resources/{id}- Update resource mappingDELETE /mappings/resources/by-key/{bridge_from}/{source_calendar_id}/{target_calendar_id}- Delete resource mapping by composite keyGET /mappings/resources/by-resource/{source_calendar_id}- Get mappings by booking system resource ID
POST /bridges/sync-deletions- Detect and sync deletions across bridge systemsPOST /bridges/process-deletion-queue- Process webhook-based deletion notifications
GET /health- Quick health checkGET /health/system- Comprehensive system healthGET /health/dashboard- Dashboard dataGET /health/sync-status- Detailed sync statusPOST /health/re-enable-failed- Re-enable failed events (all bridges)POST /bridges/process-pending-syncs[/{bridge}]- Process pending syncsPOST /bridges/re-enable-failed[/{bridge}]- Re-enable failed eventsGET /bridges/sync-stats[/{bridge}]- Sync statisticsGET /bridges/cancelled-events[/{bridge}]- Cancelled eventsGET /bridges/{bridge}/pending-events- Events pending syncPOST /alerts/check- Run alert checksGET /alerts- Recent alertsGET /alerts/stats- Alert statisticsPOST /alerts/{id}/acknowledge- Acknowledge an alertDELETE /alerts/old- Clear old alerts
POST /maintenance/cleanup-logs- Cleanup old sync logs (query:?days=int, default 30)POST /maintenance/renew-subscriptions- Renew expiring webhook subscriptions (query:?bridge=outlook&renew_before_minutes=int&limit=int)
For complete technical documentation and API reference:
- README_BRIDGE.md - Complete technical documentation with detailed API specs, configuration examples, and implementation guides
- Database Schema - Bridge database tables and views
- Calendar Sync Service Plan - Architecture and design documentation
- Setup Scripts - Database initialization and testing tools
The following legacy endpoints have been removed or redirected to bridge equivalents:
POST /sync/to-outlook→ UsePOST /bridges/sync/booking_system/outlookPOST /sync/from-outlook→ UsePOST /bridges/sync/outlook/booking_systemGET /sync/pending-items→ UseGET /mappings/resourcesDELETE /cancel/reservation/{type}/{id}/{resourceId}→ Use bridge deletion sync (/bridges/sync-deletions)POST /cancel/bulk→ UsePOST /bridges/process-deletion-queueGET /cancel/stats→ Use health endpoints (/health/system,/bridges/sync-stats)POST /webhook/outlook-notifications→ PreferPOST /bridges/webhook/outlook(legacy is still supported)
The system supports automated processing through cron jobs that use bridge endpoints:
# Bidirectional bridge synchronization
*/5 * * * * curl -X POST http://localhost:8082/bridges/sync/booking_system/outlook \
-H "Content-Type: application/json" \
-d '{"start_date":"$(date +%Y-%m-%d)","end_date":"$(date -d \"+7 days\" +%Y-%m-%d)"}'
*/10 * * * * curl -X POST http://localhost:8082/bridges/sync/outlook/booking_system \
-H "Content-Type: application/json" \
-d '{"start_date":"$(date +%Y-%m-%d)","end_date":"$(date -d \"+7 days\" +%Y-%m-%d)"}'
# Enhanced deletion processing (recommended)
*/5 * * * * /scripts/enhanced_process_deletions.sh
# Alternative: Individual deletion sync calls
*/5 * * * * curl -X POST http://localhost:8082/bridges/process-deletion-queue
*/5 * * * * curl -X POST http://localhost:8082/bridges/sync-deletions
# Health monitoring
*/10 * * * * curl -X GET http://localhost:8082/bridges/health
*/15 * * * * curl -X GET http://localhost:8082/health/system- README_BRIDGE.md - Complete bridge documentation with API specs, booking system requirements, and examples
- CLEANUP_SUMMARY.md - Summary of code cleanup and obsolete routes
- Calendar Sync Service Plan - Architecture and design documentation
- Database Schema - Bridge database tables and views
- Setup Script - Database initialization
- Test Script - API endpoint testing
- Deletion Processor - Automated deletion sync
- Sync Usage Guide - Original sync documentation
- Cancellation Detection - Cancellation handling
- Monitoring System - Health monitoring
Generic Calendar Bridge (Port 8080)
├── Bridge Management API (/bridges/*)
├── Resource Mapping API (/mappings/*)
├── Health Monitoring (/health/*)
├── Deletion Sync Processing
└── Webhook Handling (Real-time sync)
- ✅ Microsoft Outlook/365 (Full webhook + API support)
- ✅ Booking Systems (REST API)
- 🔄 Extensible (Add new calendar systems by implementing AbstractCalendarBridge)
- For New Users: Start with README_BRIDGE.md - Complete setup guide
- For Developers: See booking system API requirements in README_BRIDGE.md
- For Testing: Use
./test_bridge.shto validate all endpoints
# Check overall bridge health
curl -H "api_key: your_key" http://localhost:8082/bridges/health
# Test specific bridge
curl -H "api_key: your_key" http://localhost:8082/bridges/outlook/calendars
# View dashboard data (JSON)
curl -H "api_key: your_key" http://localhost:8082/health/dashboard | jq- Ensure
.envfile is properly configured (DB, Outlook, API_KEY) - Verify Microsoft Graph API permissions
- Confirm resource mappings exist before syncing
- Run deletion processor if deletions aren’t syncing
- Verify webhook subscriptions are active (if using webhooks)
- Check database connectivity and credentials
- Confirm network access to Microsoft 365
✅ Verified Production Features:
- Transaction safety with rollback support
- Zero error rate in sync operations
- Loop prevention mechanisms
- Comprehensive audit logging
- Real-time statistics and monitoring
- Graceful error handling and recovery
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
See LICENSE file for details.
OutlookBookingSync has been successfully transformed into a Generic Calendar Bridge platform:
- Bridge Pattern: Full migration to extensible bridge architecture
- Generic Interface: AbstractCalendarBridge base class implemented
- REST API: Pure REST communication for all calendar systems
- Database Schema: Complete bridge schema for mappings and configurations
- OutlookBridge: Microsoft Graph API with webhook support and resource discovery
- BookingSystemBridge: Generic booking system with REST API + DB fallback and configurable endpoints
- BridgeManager: Central orchestration service managing all bridges
- Resource Discovery: All bridges support available-resources, available-groups, and user calendar queries
- Bidirectional Sync: Events sync seamlessly between any bridge types
- Deletion Handling: Robust deletion detection and synchronization
- Real-time Webhooks: Instant updates via webhook notifications
- Resource Mapping: Calendar resource management system
- Health Monitoring: Comprehensive system monitoring and logging
- API Security: Authentication and secure endpoint access
- Clean Architecture: Obsolete code moved to
obsolete/directories - Modern API: RESTful endpoints replacing legacy interfaces
- Documentation: Complete guides and API documentation
- Production Scripts: Setup, testing, and automation tools
The bridge platform is now ready to support additional calendar systems:
- Google Calendar (implement GoogleCalendarBridge)
- CalDAV systems (implement CalDAVBridge)
- Exchange Server (implement ExchangeBridge)
- Any custom calendar system (extend AbstractCalendarBridge)