MVP.1 - Two-Way Sync + Booking Awareness
Timeline: Weeks 11-16
Status: In Design
Business Value: Unified booking visibility across all channels, occupancy awareness
Overview
MVP.1 extends the foundation built in MVP.0 by adding two-way synchronization with Hostaway. This enables TVL to not only push listing data outbound (TVL → Hostaway) but also ingest bookings and availability updates from Hostaway back into TVL. Property managers gain a unified view of all external bookings and can see occupancy at a glance.
This is a read-only booking system — bookings are imported from Hostaway but cannot be created directly in TVL. The availability calendar displays all bookings and blocks, providing complete occupancy visibility.
Core Capabilities
1. Two-Way Channel Sync (Building on MVP.0)
- Continue outbound sync: TVL → Hostaway (listings, content, availability)
- NEW: Inbound sync: Hostaway → TVL (bookings, availability blocks)
- Webhook receivers for real-time booking notifications
- Polling fallback for missed events (every 15 minutes)
- Idempotent ingestion with deduplication
- Sync conflict detection and logging
2. Booking Import & Display
- Import bookings from Hostaway with full metadata
- Store booking line items (nights, guests, dates, pricing)
- Read-only booking list view (filter by Space, date range, status)
- Booking detail view with guest info, pricing breakdown, status
- Link imported bookings to Spaces via external listing ID
- Track booking source (Hostaway, future: Airbnb, VRBO)
3. Availability Calendar (Basic)
- Unit-level availability calendar display
- Show bookings as occupied blocks
- Show manual blocks (maintenance, owner stays)
- Show imported iCal blocks from external feeds
- Color-coded status: booked, blocked, available
- Month/week/list views
- No booking creation (deferred to V1.0)
4. Inbound Sync Infrastructure
- Webhook endpoint: /api/webhooks/hostaway
- Webhook signature verification (HMAC)
- Event types: booking.created,booking.updated,booking.cancelled
- Polling job for sync reconciliation
- Inbound audit log (parallel to outbound_audit from MVP.0)
- Error handling and retry logic
5. Observability (Extended from MVP.0)
- Inbound sync metrics (success rate, latency, errors)
- Webhook delivery tracking
- Booking ingestion dashboard
- Sync conflict alerts
- Audit trail for all imported bookings
Domains Implemented
| Domain | Scope | Priority | Change from MVP.0 | 
|---|
| Identity & Tenancy | Full | CRITICAL | No change | 
| Authorization & Access | RBAC only | CRITICAL | No change | 
| Supply | Basic CRUD | CRITICAL | No change | 
| Channels & Distribution | Two-way sync | CRITICAL | Added inbound | 
| Availability & Calendar | Read-only display | HIGH | NEW domain | 
| Bookings | External only | HIGH | NEW domain | 
| Pricing | Display only | MEDIUM | NEW domain | 
| Analytics & Audit | Extended logging | HIGH | Expanded | 
Database Schema (22 Tables)
Inherited from MVP.0 (14 tables)
1-5. Identity & Tenancy (organizations, accounts, users, memberships, sessions)
6-7. Authorization (roles, permissions)
8-10. Supply (spaces, units, unit_snapshots)
11-13. Channels (channel_targets, channel_listings, outbound_audit)
14. Analytics (audit_events)
NEW for MVP.1 (8 tables)
Availability & Calendar (3 tables)
- availability_calendars- Authoritative calendar per Space
- blocks- Manual/system/imported blocks (maintenance, owner stays, iCal)
- holds- Temporary checkout locks (from MVP.0, now used for future bookings)
Bookings (3 tables)
- bookings- Imported external bookings
- booking_line_items- Nights, guests, pricing breakdown
- booking_guests- Guest metadata (name, email, phone)
Inbound Sync (2 tables)
- inbound_audit- Log of webhook/poll events
- ical_feeds- Inbound iCal feed subscriptions
Functional Requirements
FR-1: Webhook Receiver for Hostaway Events
- Given: Hostaway sends booking.created webhook
- When: TVL receives POST to /api/webhooks/hostaway
- Then: Signature verified (HMAC-SHA256)
- And: Event payload validated
- And: Booking ingestion job enqueued
- And: 200 OK returned immediately
- And: Event logged to inbound_audit
FR-2: Booking Import from Hostaway
- Given: Inbound job for booking.created event
- When: Worker processes job
- Then: Extract booking metadata (dates, guests, pricing, status)
- And: Map external listing_id → internal Space
- And: Create or update booking record
- And: Create booking_line_items (nightly breakdown)
- And: Create booking_guests record
- And: Update availability_calendar with new booking
- And: Log success/failure to inbound_audit
- And: Emit internal event: booking.imported
FR-3: Booking Update Handling
- Given: Inbound job for booking.updated event
- When: Worker processes job
- Then: Lookup existing booking by external_booking_id
- And: Compare version/updated_at timestamps
- And: Update booking fields if newer
- And: Update availability if dates changed
- And: Log change to audit_events
- And: Emit event: booking.updated
FR-4: Booking Cancellation
- Given: Inbound job for booking.cancelled event
- When: Worker processes job
- Then: Lookup existing booking
- And: Set status = 'cancelled', cancelled_at = now()
- And: Free availability on calendar
- And: Log cancellation to audit_events
- And: Emit event: booking.cancelled
FR-5: Polling Reconciliation Job
- Given: Scheduled job runs every 15 minutes
- When: Job executes
- Then: Fetch recent bookings from Hostaway API (last 24 hours)
- And: Compare with local bookings by external_booking_id
- And: Enqueue jobs for any missing/outdated bookings
- And: Log reconciliation results to inbound_audit
- And: Alert on sync lag > 1 hour
FR-6: Availability Calendar Display
- Given: User with Space access
- When: User views Space calendar
- Then: Render month/week view
- And: Show bookings as solid blocks (color: blue)
- And: Show manual blocks (color: gray)
- And: Show holds if active (color: yellow, dotted)
- And: Show available dates (color: white/green)
- And: Display booking details on hover/click
- And: Filter by date range
FR-7: Booking List View
- Given: User with Viewer+ role
- When: User navigates to Bookings page
- Then: Display table of all bookings
- And: Columns: Space, Guest, Check-in, Check-out, Nights, Status, Source
- And: Filter by: Space, date range, status, source channel
- And: Sort by: check-in date (default), created_at, status
- And: Paginate (50 per page)
FR-8: Booking Detail View
- Given: User clicks on a booking
- When: Detail modal/page loads
- Then: Display full booking metadata:
- Space name and photo
- Guest name, email, phone
- Check-in/check-out dates
- Nights, guests (adults/children)
- Total price, breakdown (nightly, fees, taxes)
- Status, source channel
- External booking ID (Hostaway reference)
- Created/updated timestamps
 
- And: Show sync status (last synced, any errors)
- And: Link to Space and channel_listing
FR-9: iCal Feed Import (External Calendars)
- Given: User adds external iCal feed URL (e.g., from Airbnb)
- When: Import job runs (hourly)
- Then: Fetch feed via HTTP GET
- And: Parse iCal events (VEVENT blocks)
- And: Create/update blocks for each event
- And: Set source = 'external', external_ical_uid
- And: Deduplicate by (calendar_id, external_ical_uid)
- And: Use If-None-Match (ETag) to skip unchanged feeds
- And: Log import to inbound_audit
FR-10: Idempotent Booking Ingestion
- Given: Duplicate webhook for same booking
- When: Ingestion job processes duplicate event
- Then: Lookup by external_booking_id
- And: Compare external_updated_at timestamp
- And: Skip if local record is newer or equal
- And: Log skipped duplicate to inbound_audit
- And: Return success (no error)
Non-Functional Requirements
- Webhook response time: <200ms (ack only, async processing)
- Booking ingestion latency: <30 seconds (webhook to visible)
- Polling reconciliation: <2 minutes per sync cycle
- Calendar load time: <500ms for 90-day view
NFR-2: Reliability
- Webhook delivery: at-least-once processing via job queue
- Idempotency: safe to replay events (dedupe by external_id + timestamp)
- Polling fallback: catches missed webhooks within 15 minutes
- Zero data loss: all events logged to inbound_audit
NFR-3: Data Consistency
- Bookings always linked to correct Space (via external listing_id)
- Availability conflicts detected and logged (booking overlaps)
- Sync conflicts resolved: external source wins (Hostaway is source of truth)
- Audit trail: every booking create/update/cancel logged
NFR-4: Security
- Webhook signatures verified (HMAC-SHA256 with shared secret)
- API tokens stored in Secrets Manager
- Rate limiting: 100 req/min per channel target
- Webhook endpoint: HTTPS only, no public exposure without auth
NFR-5: Scalability
- Support 50+ properties with 200+ annual bookings each
- Handle 1000+ webhook events/day
- Process 500+ imported iCal events/hour
- Prepare for 1000+ properties in MVP.2
Technical Architecture
Stack (Inherited from MVP.0)
- Backend: Node.js 20+ with TypeScript
- Framework: Express.js
- Database: PostgreSQL 15+
- Queue: BullMQ + Redis
- Auth: Google OIDC (Passport.js)
- Secrets: AWS Secrets Manager or GCP Secret Manager
- Deployment: Docker + Kubernetes
NEW Components for MVP.1
| Component | Technology | Purpose | 
|---|
| Webhook receiver | Express route + middleware | Accept Hostaway events | 
| Signature verifier | crypto (Node.js) | HMAC-SHA256 validation | 
| Inbound job processor | BullMQ worker | Process booking events | 
| Polling reconciliation | Cron job (node-cron) | Catch missed events | 
| iCal parser | ical.js | Parse external feeds | 
| Calendar UI | React + date-fns | Display availability | 
| Booking UI | React table | List and detail views | 
Key Design Decisions
| Decision | Choice | Rationale | 
|---|
| Sync direction | Two-way (outbound + inbound) | Enable booking visibility | 
| Booking creation | External only (no direct bookings) | Defer to V1.0; reduce MVP scope | 
| Webhook vs polling | Both (webhook primary, polling backup) | Reliability + real-time updates | 
| Conflict resolution | External source wins | Hostaway is source of truth for MVP | 
| Calendar granularity | Unit-level (Space = 1 Unit in MVP) | Simplify; multi-unit in V1.0 | 
| Availability storage | Dedicated calendar + blocks tables | Clean separation from bookings | 
| iCal handling | Import as blocks, not bookings | External events lack booking metadata | 
| Idempotency | Dedupe by external_id + timestamp | Prevent duplicate ingestion | 
Success Metrics
| Metric | Target | Measurement | 
|---|
| Bookings imported | 100% of Hostaway bookings | Compare Hostaway API vs TVL DB | 
| Webhook success rate | 99%+ | inbound_audit success % | 
| Sync latency | <1 min (webhook), <15 min (polling) | Time from external event to visible | 
| Calendar accuracy | 100% match with Hostaway | Manual spot checks | 
| Zero booking loss | 100% | All external bookings ingested | 
| Availability conflicts | <1% false positives | Conflict alert rate | 
| User adoption | 10+ properties using calendar | Active Space count | 
Out of Scope (Deferred)
Deferred to MVP.2
- Multi-channel inbound (Airbnb, VRBO)
- Advanced conflict resolution (automated)
- Channel-specific pricing display
- Booking modification (dates, guests)
Deferred to V1.0
- Direct booking creation in TVL
- Payment processing
- Guest communication (messages)
- Owner financial reports (revenue splits)
- Dynamic pricing rules
- Multi-unit bookings
Deferred to V2.0+
- Event/experience bookings
- Booking recommendations (AI)
- Guest portal
- Task automation (cleaning, maintenance)
Dependencies
External Dependencies
- Hostaway Webhooks API (docs.hostaway.com/webhooks)
- Hostaway REST API v1 (for polling reconciliation)
- iCal format (RFC 5545) for external feeds
- Google OIDC (from MVP.0)
- Secrets Manager (from MVP.0)
Internal Dependencies
- MVP.0 infrastructure (Auth, Multi-tenancy, Supply, Channels)
- channel_targets table (Hostaway connection config)
- channel_listings table (external listing_id mapping)
- spaces table (booking destination)
Risks & Mitigation
| Risk | Impact | Probability | Mitigation | 
|---|
| Webhook delivery failures | HIGH | MEDIUM | Polling fallback every 15 min | 
| External listing_id mismatch | HIGH | MEDIUM | Validation on import; manual override UI | 
| Booking conflicts (double-booking) | HIGH | LOW | Detect and alert; manual resolution | 
| Hostaway API rate limits | MEDIUM | MEDIUM | Respect 429 responses; exponential backoff | 
| Webhook signature spoofing | HIGH | LOW | HMAC verification; rotate secrets | 
| iCal feed downtime | MEDIUM | MEDIUM | Retry on 5xx; alert on 24hr failure | 
| Data inconsistency (stale bookings) | MEDIUM | MEDIUM | Polling reconciliation; audit logs | 
Acceptance Criteria
Delivery Plan
Week 11: Inbound Infrastructure
- Database schema (8 new tables)
- Migrations
- Webhook endpoint with signature verification
- Inbound audit logging
Week 12: Booking Import
- Booking ingestion worker
- booking.created, booking.updated, booking.cancelled handlers
- External listing_id → Space mapping
- booking_line_items and booking_guests creation
Week 13: Polling & Reconciliation
- Polling job (every 15 minutes)
- Reconciliation logic (compare external vs local)
- Conflict detection and alerting
- iCal feed import (basic)
Week 14: Availability Calendar
- availability_calendars and blocks tables
- Calendar API (get availability by date range)
- Calendar UI (month/week views)
- Color-coded blocks (booked, blocked, available)
Week 15: Booking UI
- Booking List view (table with filters)
- Booking Detail view (modal/page)
- Sync status indicators
- Testing with real Hostaway data
Week 16: Testing & Hardening
- Integration tests (webhook → ingestion → display)
- Load testing (1000+ webhooks/day)
- Security review (signature verification)
- Bug fixes and polish
- Staging validation
- Production deployment