# PRD: Veeam Service Provider Console (VSPC) Backup Integration

## Introduction/Overview

This feature adds Veeam Service Provider Console (VSPC) as a connected vendor in Pulse, providing visibility into backup job status, protected workloads, backup repositories, and restore point data across all managed clients. Data will be synced on a schedule to PostgreSQL (following the same pattern as the Autotask sync) and displayed on a dedicated **Backup Status** page.

Additionally, this integration includes a **Contract Compliance** audit that cross-references Autotask configuration items (which have a backup-type UDF and are associated with active contracts) against actual Veeam backup data. This ensures every contracted backup is actually running, and every active Veeam backup is covered by a contract.

**Problem Statement:** MSP technicians and administrators currently have no visibility into backup health within Pulse. They must log into the Veeam Service Provider Console separately to check backup job statuses, identify failed backups, verify protected machines, and assess repository capacity. This context-switching slows response times and makes it easy to miss backup failures. Furthermore, there is no automated way to verify that devices under an active backup contract are actually being backed up, or that Veeam is not backing up devices that aren't under contract — leading to revenue leakage and compliance gaps.

**Goal:** Provide a centralized Backup Status page within Pulse that surfaces all critical Veeam backup data — job statuses, protected/unprotected machines, repository usage, restore point history, and RPO/SLA compliance — synced on a configurable schedule and linked to Autotask companies via the Company ID field in VSPC. Additionally, provide a Contract Compliance view that flags mismatches between contracted backup services and actual Veeam protection.

## Goals

1. **Visibility Goal:** Surface backup job status (success/failure/warning) for all managed clients in a single page
2. **Coverage Goal:** Identify protected vs unprotected machines per company to highlight backup gaps
3. **Capacity Goal:** Display backup repository usage and capacity to enable proactive storage management
4. **Compliance Goal:** Show RPO/SLA compliance status per company and per protected workload
5. **Contract Compliance Goal:** Cross-reference Autotask config items with backup UDFs on active contracts against Veeam protected workloads to identify: (a) contracted devices not being backed up, and (b) devices being backed up without a contract
6. **Sync Goal:** Reliably sync Veeam data to PostgreSQL on a configurable schedule, following the existing Autotask sync pattern
7. **Matching Goal:** Automatically associate VSPC organizations with Autotask companies using the Company ID field stored in VSPC
8. **UDF Sync Goal:** Sync the backup-type UDF (ID: `29693319`) from Autotask configuration items to PostgreSQL to enable contract compliance analysis

## User Stories

1. **As an MSP administrator**, I want to see a summary of all backup job statuses across all clients on a single page, so that I can quickly identify which clients have failing backups without logging into VSPC.

2. **As an MSP technician**, I want to view detailed backup information for a specific company — including last backup time, job status, and protected machines — so that I can troubleshoot backup issues efficiently.

3. **As an MSP administrator**, I want to see which machines are not protected by any backup job, so that I can ensure complete backup coverage for all clients.

4. **As an MSP administrator**, I want to monitor backup repository usage and capacity, so that I can proactively provision additional storage before repositories fill up.

5. **As an MSP administrator**, I want to see restore point history for protected machines, so that I can verify data recoverability and RPO compliance.

6. **As an MSP administrator**, I want backup data to sync automatically on a schedule, so that the Backup Status page always shows recent data without manual intervention.

7. **As an MSP administrator**, I want to configure the Veeam sync schedule from the admin panel, so that I can control how frequently data is refreshed.

8. **As an MSP administrator**, I want to see which configuration items have a backup UDF set (Server Image, Workstation Image, Workstation File Based, etc.) and are on an active contract but are NOT being backed up in Veeam, so that I can identify contracted services that aren't being delivered.

9. **As an MSP administrator**, I want to see which machines are being backed up in Veeam but do NOT have a corresponding configuration item with a backup UDF on an active contract, so that I can identify unbilled backup services and potential revenue leakage.

10. **As an MSP administrator**, I want a summary card showing the total number of contract compliance mismatches, so that I can quickly assess the overall health of backup contract alignment.

## Functional Requirements

### FR1: Veeam VSPC API Client

1.1. Create a Veeam VSPC API client service (`/lib/services/veeam-client.ts`) that handles authentication and API requests
1.2. Use credentials from environment variables: `VEEAM_VSPC_URL`, `VEEAM_VSPC_API_KEY`
1.3. Implement API Key-based authorization using the `Authorization: Bearer <API-Key>` header
1.4. Base URL format: `https://<hostname>:1280/api/v3`
1.5. Support pagination using `offset` and `limit` query parameters
1.6. Support filtering using the VSPC `filter` query parameter syntax
1.7. Include proper error handling and logging for all API calls
1.8. Implement rate limiting to respect VSPC throttling settings
1.9. Handle token refresh if using OAuth 2.0 as a fallback authentication method

### FR2: VSPC Data Fetching

Fetch the following data from the VSPC REST API v3:

2.1. **Organizations** — `/organizations` — Company/tenant data including `instanceUid`, `name`, `companyId` (maps to Autotask Company ID)
2.2. **Backup Servers** — `/infrastructure/backupServers` — Server name, version, status, role type
2.3. **Backup Jobs** — Backup job definitions including name, type, schedule, target repository, associated organization
2.4. **Backup Job Sessions** — Recent job session results including status (Success/Warning/Failed), start time, end time, duration, transferred data size, bottleneck info
2.5. **Protected Workloads** — Protected virtual machines and physical servers including name, platform, protection status, last restore point date
2.6. **Backup Repositories** — Repository name, capacity, free space, used space, associated backup server
2.7. **Restore Points** — Restore point data per protected workload including creation date, size, type (full/incremental)

### FR3: Company Matching

3.1. Match VSPC organizations to Autotask companies using the `companyId` field in VSPC, which contains the Autotask Company ID
3.2. Store the mapping in the `veeam_organizations` database table
3.3. Handle cases where `companyId` is not set in VSPC (log warning, skip matching)
3.4. Support manual mapping override via admin UI for edge cases

### FR4: PostgreSQL Database Schema

Create the following tables for synced Veeam data:

4.1. **`veeam_organizations`** — VSPC organization data
  - `instance_uid` (VARCHAR, PK) — VSPC organization UID
  - `name` (VARCHAR) — Organization name
  - `company_id` (INTEGER) — Autotask Company ID (FK reference)
  - `status` (VARCHAR) — Organization status
  - `synced_at` (TIMESTAMP)

4.2. **`veeam_backup_servers`** — Backup server infrastructure
  - `instance_uid` (VARCHAR, PK)
  - `name` (VARCHAR)
  - `organization_uid` (VARCHAR, FK → veeam_organizations)
  - `version` (VARCHAR)
  - `display_version` (VARCHAR)
  - `status` (VARCHAR) — Healthy/Warning/Error
  - `role_type` (VARCHAR) — CloudConnect/Hosted/etc.
  - `synced_at` (TIMESTAMP)

4.3. **`veeam_backup_jobs`** — Backup job definitions
  - `instance_uid` (VARCHAR, PK)
  - `name` (VARCHAR)
  - `organization_uid` (VARCHAR, FK → veeam_organizations)
  - `backup_server_uid` (VARCHAR, FK → veeam_backup_servers)
  - `job_type` (VARCHAR) — Backup/Replication/Copy/etc.
  - `status` (VARCHAR) — Running/Idle/Disabled
  - `last_run` (TIMESTAMP)
  - `last_result` (VARCHAR) — Success/Warning/Failed
  - `schedule_enabled` (BOOLEAN)
  - `repository_uid` (VARCHAR)
  - `synced_at` (TIMESTAMP)

4.4. **`veeam_job_sessions`** — Recent backup job session results
  - `instance_uid` (VARCHAR, PK)
  - `job_uid` (VARCHAR, FK → veeam_backup_jobs)
  - `organization_uid` (VARCHAR, FK → veeam_organizations)
  - `status` (VARCHAR) — Success/Warning/Failed
  - `start_time` (TIMESTAMP)
  - `end_time` (TIMESTAMP)
  - `duration_seconds` (INTEGER)
  - `transferred_bytes` (BIGINT)
  - `processed_bytes` (BIGINT)
  - `bottleneck` (VARCHAR)
  - `error_message` (TEXT)
  - `synced_at` (TIMESTAMP)

4.5. **`veeam_protected_workloads`** — Protected VMs and physical servers
  - `instance_uid` (VARCHAR, PK)
  - `name` (VARCHAR)
  - `organization_uid` (VARCHAR, FK → veeam_organizations)
  - `platform` (VARCHAR) — VMware/Hyper-V/Physical/etc.
  - `protection_status` (VARCHAR) — Protected/Unprotected/Partial
  - `last_restore_point` (TIMESTAMP)
  - `restore_point_count` (INTEGER)
  - `total_backup_size_bytes` (BIGINT)
  - `synced_at` (TIMESTAMP)

4.6. **`veeam_repositories`** — Backup repository capacity
  - `instance_uid` (VARCHAR, PK)
  - `name` (VARCHAR)
  - `backup_server_uid` (VARCHAR, FK → veeam_backup_servers)
  - `capacity_bytes` (BIGINT)
  - `free_space_bytes` (BIGINT)
  - `used_space_bytes` (BIGINT)
  - `repository_type` (VARCHAR)
  - `synced_at` (TIMESTAMP)

4.7. **`veeam_restore_points`** — Restore point history
  - `instance_uid` (VARCHAR, PK)
  - `workload_uid` (VARCHAR, FK → veeam_protected_workloads)
  - `organization_uid` (VARCHAR, FK → veeam_organizations)
  - `creation_date` (TIMESTAMP)
  - `type` (VARCHAR) — Full/Incremental
  - `size_bytes` (BIGINT)
  - `synced_at` (TIMESTAMP)

### FR5: Veeam Sync Service

5.1. Create a Veeam sync service (`/lib/services/veeam-sync-service.ts`) following the pattern of the existing Autotask `SyncService`
5.2. Support full sync (all data) and incremental sync (changed data since last sync)
5.3. Sync entities in dependency order: organizations → backup servers → repositories → backup jobs → job sessions → protected workloads → restore points
5.4. Record sync history in the existing `sync_history` table with `sync_source: 'veeam'`
5.5. Log sync progress and errors using the existing `SyncLogger` pattern
5.6. Handle API pagination automatically (fetch all pages per entity)

### FR6: Sync Scheduler Integration

6.1. Add Veeam sync schedules to the existing sync scheduler (`/lib/services/sync-scheduler.ts`)
6.2. Default schedule: incremental sync every 30 minutes, full sync daily at 2:00 AM
6.3. Allow schedule configuration from the Admin → Sync page
6.4. Display Veeam sync status and history alongside Autotask sync data in the admin UI

### FR7: Backup Status Page

7.1. Create a new page at `/backup-status` accessible from the main navigation
7.2. Page layout sections:

**Summary Cards (top row):**
  - Total protected workloads count
  - Unprotected workloads count (with warning color if > 0)
  - Last 24h job success rate (percentage)
  - Failed jobs in last 24h (count, red if > 0)
  - Total repository usage (used/total with percentage bar)

**Company Backup Overview (main table):**
  - Company name (linked to Autotask company)
  - Protected workload count
  - Unprotected workload count
  - Last backup job status (color-coded badge: green/yellow/red)
  - Last successful backup timestamp
  - Oldest restore point age
  - Repository usage for company (if applicable)
  - Expandable row to show individual workloads and jobs

**Filters:**
  - Filter by company
  - Filter by backup status (Success/Warning/Failed/All)
  - Filter by protection status (Protected/Unprotected/All)
  - Search by workload name

### FR8: Company Backup Detail View

8.1. Clicking a company row expands to show:
  - List of all protected workloads with last restore point date and status
  - List of all backup jobs with last run time and result
  - Recent job session history (last 7 days) with status timeline
  - Unprotected machines highlighted in red/warning

### FR9: API Endpoints

9.1. `GET /api/veeam/backup-status` — Summary statistics for dashboard cards
9.2. `GET /api/veeam/companies` — Company-level backup overview with aggregated stats
9.3. `GET /api/veeam/companies/[companyId]/workloads` — Protected workloads for a specific company
9.4. `GET /api/veeam/companies/[companyId]/jobs` — Backup jobs for a specific company
9.5. `GET /api/veeam/companies/[companyId]/sessions` — Recent job sessions for a specific company
9.6. `GET /api/veeam/repositories` — Repository capacity overview
9.7. `GET /api/veeam/sync` — Trigger manual Veeam sync (POST)
9.8. `GET /api/veeam/compliance` — Contract compliance summary and mismatch list
9.9. `GET /api/veeam/compliance/[companyId]` — Compliance details for a specific company
9.10. All endpoints read from PostgreSQL (synced data), not directly from VSPC API

### FR10: TypeScript Types

10.1. Create Veeam type definitions in `/lib/types/veeam.ts`:
  - `VeeamOrganization` interface
  - `VeeamBackupServer` interface
  - `VeeamBackupJob` interface
  - `VeeamJobSession` interface
  - `VeeamProtectedWorkload` interface
  - `VeeamRepository` interface
  - `VeeamRestorePoint` interface
  - `VeeamBackupStatusSummary` interface (for dashboard cards)
  - `VeeamCompanyBackupOverview` interface (for company table)
  - `VeeamComplianceResult` interface (for compliance mismatch records)
  - `VeeamComplianceSummary` interface (for compliance summary cards)
10.2. Export types for use across the application

### FR11: Error Handling & Logging

11.1. Log all VSPC API errors to console with descriptive messages
11.2. Log sync progress (entities synced, records created/updated/skipped)
11.3. Never throw errors that would break the Backup Status page — show "Data unavailable" gracefully
11.4. Display last successful sync timestamp on the Backup Status page
11.5. Show warning banner if last sync is older than 2 hours

### FR12: Navigation Integration

12.1. Add "Backup Status" link to the main sidebar navigation
12.2. Use an appropriate Lucide icon (e.g., `HardDrive`, `Shield`, `Database`)
12.3. Position after existing navigation items

### FR13: Autotask Backup UDF Sync

**Note:** Autotask configuration item UDFs are NOT currently synced to PostgreSQL. The `mapConfigurationItem` function in `/lib/utils/entity-mapper.ts` does not include `userDefinedFields`. This must be addressed.

13.1. Add a `backup_type_udf` column (VARCHAR, nullable) to the `configuration_items` table via migration
13.2. During Autotask config item sync, extract the backup-type UDF (ID: `29693319`) from the `userDefinedFields` array and store its value in the `backup_type_udf` column
13.3. Update `mapConfigurationItem()` in `/lib/utils/entity-mapper.ts` to map the UDF value
13.4. Known UDF values to support:
   - `Server Image`
   - `Workstation Image`
   - `Workstation File Based`
   - Any other non-null/non-empty value should also be stored as-is
13.5. Add an index on `backup_type_udf` for efficient compliance queries
13.6. Ensure the Autotask API query for configuration items includes `userDefinedFields` in the response (verify the existing `queryEntity` call returns UDFs)

### FR14: Contract Compliance Logic

14.1. Define a config item as "contracted for backup" when ALL of the following are true:
   - `backup_type_udf` is NOT NULL and NOT empty
   - `contract_id` is NOT NULL
   - The associated contract has `status` = active (numeric value TBD — verify from Autotask picklist)
   - `is_active` = true
14.2. Match contracted config items to Veeam protected workloads using:
   - Primary: hostname match (config item `reference_title` or RMM hostname vs Veeam workload `name`)
   - Secondary: company match + name similarity (fuzzy)
14.3. Produce two mismatch lists per company:
   - **Contracted but NOT backed up:** Config items meeting 14.1 criteria with no matching Veeam protected workload
   - **Backed up but NOT contracted:** Veeam protected workloads with no matching config item that meets 14.1 criteria
14.4. Store compliance results in a `veeam_compliance_results` table:
   - `id` (SERIAL, PK)
   - `company_id` (INTEGER, FK → companies)
   - `configuration_item_id` (INTEGER, nullable, FK → configuration_items)
   - `veeam_workload_uid` (VARCHAR, nullable, FK → veeam_protected_workloads)
   - `mismatch_type` (VARCHAR) — `contracted_not_backed_up` or `backed_up_not_contracted`
   - `backup_type_udf` (VARCHAR, nullable) — The UDF value from the config item
   - `device_name` (VARCHAR) — Name of the device for display
   - `computed_at` (TIMESTAMP) — When this compliance check was run
14.5. Recompute compliance results after each Veeam sync completes
14.6. Log compliance computation results (total matched, total mismatches per type)

### FR15: Contract Compliance UI

15.1. Add a "Contract Compliance" tab to the Backup Status page (alongside the main backup overview)
15.2. **Compliance Summary Cards:**
   - Total contracted backup devices (config items with backup UDF + active contract)
   - Matched (contracted AND backed up in Veeam)
   - Contracted but NOT backed up (red/warning count)
   - Backed up but NOT contracted (amber/warning count)
15.3. **Compliance Detail Table:**
   - Company name
   - Device name
   - Mismatch type (visual badge: "Missing Backup" in red, "No Contract" in amber)
   - Backup type UDF value (Server Image, Workstation Image, etc.)
   - Contract name (if applicable)
   - Veeam workload name (if applicable)
   - Filterable by company, mismatch type
15.4. **Company Backup Detail** (FR8) should also show compliance status:
   - In the expanded company row, show a "Compliance" section listing any mismatches for that company

## Non-Goals (Out of Scope)

1. **Kiosk/Ticker Integration:** Backup data will NOT appear in the kiosk ticker or dashboard
2. **Backup Triggering:** Will NOT allow starting/stopping backup jobs from Pulse
3. **Restore Operations:** Will NOT support initiating restores from Pulse
4. **Auto-Remediation:** Will NOT automatically create contracts or backup jobs to fix compliance mismatches
5. **Alerting/Notifications:** Will NOT send email or other notifications for backup failures
6. **Veeam Cloud Connect:** Will NOT integrate with Cloud Connect tenant data specifically
7. **Real-Time Data:** All data is synced on schedule; no real-time/on-demand VSPC API calls from the UI
8. **Multi-Instance:** Will NOT support multiple VSPC instances (single instance only)

## Design Considerations

### UI Components
- Follow existing Pulse design patterns (dark theme, shadcn/ui components)
- Use same Card, Badge, Table, and Button components from shadcn/ui
- Maintain consistent spacing, typography, and color scheme
- Use Lucide icons for backup-related visuals

### Status Color Coding
- **Success:** Green badge/indicator
- **Warning:** Yellow/amber badge/indicator
- **Failed:** Red badge/indicator
- **Running:** Blue badge/indicator with spinner
- **Disabled:** Gray badge/indicator

### Page Layout
```
┌──────────────────────────────────────────────────────────────┐
│  Backup Status                              Last sync: 5m ago │
├──────────────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────┐│
│ │Protected │ │Unprotect.│ │Success % │ │ Failed   │ │Repo ││
│ │  142     │ │  3  ⚠    │ │  96.2%   │ │  4  🔴   │ │ 72% ││
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └─────┘│
├──────────────────────────────────────────────────────────────┤
│ [Filter: Company ▼] [Status ▼] [Protection ▼] [Search...]   │
├──────────────────────────────────────────────────────────────┤
│ Company        │ Protected │ Unprotected │ Status │ Last OK  │
│────────────────┼───────────┼─────────────┼────────┼──────────│
│ Acme Corp      │    12     │     0       │  ✅    │ 2h ago   │
│  └─ [Expand to show workloads and jobs]                      │
│ Beta LLC       │     8     │     1       │  ⚠️    │ 5h ago   │
│ Gamma Inc      │     5     │     0       │  🔴    │ 26h ago  │
└──────────────────────────────────────────────────────────────┘
```

### Repository Capacity Visualization
- Use progress bars showing used/total capacity
- Color-code: green (< 70%), yellow (70-85%), red (> 85%)

### Contract Compliance Tab Layout
```
┌──────────────────────────────────────────────────────────────┐
│  [Backup Overview]  [Contract Compliance]                     │
├──────────────────────────────────────────────────────────────┤
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Contracted │ │  Matched   │ │ Missing    │ │    No      │ │
│ │   Backups  │ │            │ │  Backup    │ │ Contract   │ │
│ │    87      │ │  82  ✅    │ │  3  🔴     │ │  2  🟠     │ │
│ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │
├──────────────────────────────────────────────────────────────┤
│ [Filter: Company ▼] [Mismatch Type ▼] [Search...]            │
├──────────────────────────────────────────────────────────────┤
│ Company     │ Device         │ Type     │ Backup UDF  │Issue │
│─────────────┼────────────────┼──────────┼─────────────┼──────│
│ Acme Corp   │ ACME-FS01      │ 🔴 Miss. │ Server Image│ No   │
│             │                │  Backup  │             │Veeam │
│ Beta LLC    │ BETA-PC-042    │ 🟠 No    │             │ No   │
│             │                │ Contract │             │ UDF  │
└──────────────────────────────────────────────────────────────┘
```

### Compliance Status Badges
- **Matched:** Green badge — contracted and backed up
- **Missing Backup:** Red badge — contracted but not found in Veeam
- **No Contract:** Amber badge — backed up in Veeam but no matching contract

## Technical Considerations

### VSPC API Integration
- **API Version:** REST API v3 (3.6.1)
- **Base URL:** `https://<hostname>:1280/api/v3`
- **Authentication:** API Key-based (`Authorization: Bearer <API-Key>`)
- **Pagination:** Offset-based with `offset` and `limit` query parameters
- **Filtering:** VSPC filter syntax (e.g., `filter=status eq "Failed"`)
- **Rate Limiting:** Respect VSPC throttling settings; implement exponential backoff
- **SSL:** VSPC may use self-signed certificates; support `NODE_TLS_REJECT_UNAUTHORIZED` env var for development

### Company Matching Logic
```typescript
// VSPC organizations have a companyId field that maps to Autotask Company ID
async function matchOrganizations(
  vspcOrgs: VeeamOrganization[],
  autotaskCompanies: Company[]
): Map<string, number> {
  const mapping = new Map<string, number>(); // vspcOrgUid → autotaskCompanyId
  
  for (const org of vspcOrgs) {
    if (org.companyId) {
      const match = autotaskCompanies.find(c => c.id === parseInt(org.companyId));
      if (match) {
        mapping.set(org.instanceUid, match.id);
      }
    }
  }
  
  return mapping;
}
```

### Dependencies
- No new npm packages required (use built-in fetch)
- Leverage existing service patterns (`autotask-client.ts`, `sync-service.ts`, `entity-sync.ts`)
- Use existing UI components from shadcn/ui
- Use existing PostgreSQL client (`postgres-client.ts`)
- Use existing sync scheduler infrastructure

### File Structure
```
/lib/services/veeam-client.ts              # VSPC API client
/lib/services/veeam-factory.ts             # Singleton factory for client
/lib/services/veeam-sync-service.ts        # Sync orchestration service
/lib/services/veeam-compliance-service.ts  # Contract compliance computation
/lib/types/veeam.ts                        # TypeScript types
/app/api/veeam/backup-status/route.ts      # Summary stats endpoint
/app/api/veeam/companies/route.ts          # Company backup overview
/app/api/veeam/companies/[companyId]/workloads/route.ts
/app/api/veeam/companies/[companyId]/jobs/route.ts
/app/api/veeam/companies/[companyId]/sessions/route.ts
/app/api/veeam/repositories/route.ts       # Repository capacity
/app/api/veeam/sync/route.ts               # Manual sync trigger
/app/api/veeam/compliance/route.ts         # Compliance summary + list
/app/api/veeam/compliance/[companyId]/route.ts  # Per-company compliance
/app/backup-status/page.tsx                # Main Backup Status page
/components/backup/backup-summary-cards.tsx # Summary KPI cards
/components/backup/company-backup-table.tsx # Company overview table
/components/backup/company-backup-detail.tsx # Expanded company detail
/components/backup/repository-overview.tsx  # Repository capacity view
/components/backup/compliance-summary-cards.tsx  # Compliance KPI cards
/components/backup/compliance-detail-table.tsx   # Compliance mismatch table
/migrations/023_create_veeam_tables.sql     # Veeam tables + compliance results
/migrations/024_add_backup_type_udf.sql     # Add backup_type_udf to configuration_items
```

### Environment Variables
```bash
VEEAM_VSPC_URL=https://vac.wulfconsulting.com:1280
VEEAM_VSPC_API_KEY=your-api-key-here
# Optional: set to '0' if VSPC uses self-signed certs (dev only)
# NODE_TLS_REJECT_UNAUTHORIZED=0
```

### Database Migration
- Migration file: `023_create_veeam_tables.sql`
  - Creates all 7 Veeam tables with appropriate indexes
  - Creates `veeam_compliance_results` table
  - Add indexes on `organization_uid`, `company_id`, `status`, `synced_at` columns for query performance
  - Add index on `veeam_job_sessions.start_time` for time-range queries
  - Add index on `veeam_compliance_results.company_id` and `mismatch_type`
- Migration file: `024_add_backup_type_udf.sql`
  - Adds `backup_type_udf` (VARCHAR, nullable) column to `configuration_items` table
  - Adds index on `backup_type_udf` for compliance queries
  - Note: Existing config items will have NULL for this column until the next Autotask sync runs

## Success Metrics

1. **Sync Reliability:** 99%+ of scheduled syncs complete successfully
2. **Data Freshness:** Backup status data is never more than 1 hour old during business hours
3. **Company Match Rate:** 95%+ of VSPC organizations are matched to Autotask companies via Company ID
4. **Page Load Performance:** Backup Status page loads within 2 seconds for typical MSP (< 50 companies)
5. **Coverage Visibility:** 100% of VSPC-managed workloads are visible in the Backup Status page
6. **Adoption:** Administrators check the Backup Status page at least once daily within the first month
7. **Compliance Accuracy:** 95%+ of contracted backup devices are correctly matched to Veeam workloads (hostname matching)
8. **Revenue Protection:** Identify 100% of Veeam-backed workloads that lack a corresponding active contract

## Open Questions

1. **VSPC API Key Permissions:** What permission level is needed for the API key? Read-only access to all organizations?
   - *Recommendation:* Create an API key with `rest` scope and `isReadAccessOnly: true`

2. **Session History Depth:** How many days of job session history should we sync and retain?
   - *Recommendation:* Sync last 30 days, retain 90 days, purge older records

3. **Self-Signed Certificates:** Does the VSPC instance use a self-signed SSL certificate?
   - *Recommendation:* Support `NODE_TLS_REJECT_UNAUTHORIZED` env var; document in setup guide

4. **VSPC Version Compatibility:** Is the VSPC instance running v9.1 (REST API 3.6.1) or a different version?
   - *Recommendation:* Target v3 API which is stable across recent VSPC versions

5. **Unprotected Machine Detection:** How should "unprotected" be defined — machines with no backup job, or machines whose last backup is older than X hours?
   - *Recommendation:* Both — flag machines with no job AND machines with stale backups (configurable threshold, default 24h)

6. **Repository Scope:** Should we show all repositories or only those relevant to managed clients?
   - *Recommendation:* Show all repositories with filtering by backup server

7. **UDF Field ID Verification:** The backup-type UDF is believed to be ID `29693319`. This needs to be verified against the Autotask API by querying the UDF field definitions for ConfigurationItems.
   - *Action:* Query `GET /atservicesrest/v1.0/ConfigurationItemUserDefinedFields` or inspect a config item with a known backup UDF value to confirm the field ID and name

8. **Contract Active Status Value:** What is the numeric value for "Active" contract status in Autotask?
   - *Action:* Query the Autotask picklist for contract statuses to determine the correct numeric value

9. **Hostname Matching Accuracy:** Config item `reference_title` may not always match the Veeam workload `name` exactly. What fallback matching strategies should be used?
   - *Recommendation:* Try `reference_title`, then `rmmDeviceAuditHostname`, then `serial_number`. Allow manual override mapping in a future iteration

10. **UDF Values Completeness:** Are "Server Image", "Workstation Image", and "Workstation File Based" the only backup UDF values, or are there others?
    - *Action:* Query Autotask for the UDF picklist values to get the complete list

## Implementation Notes for Developers

### Getting Started
1. Review existing Autotask sync service (`sync-service.ts`, `entity-sync.ts`) as the primary reference pattern
2. Review VSPC REST API documentation: https://helpcenter.veeam.com/references/vac/9.1/rest/3.6.1/tag/SectionAbout
3. Set up VSPC API key with read-only REST access
4. Test API connectivity using curl or Postman before coding
5. Run the database migration to create Veeam tables

### Sync Implementation Order
1. Implement `veeam-client.ts` with authentication and basic GET requests
2. Implement organization sync first (simplest entity, needed for FK references)
3. Add backup servers and repositories
4. Add backup jobs and job sessions
5. Add protected workloads and restore points
6. Wire up to sync scheduler
7. Add `backup_type_udf` column migration and update `mapConfigurationItem()` in entity-mapper
8. Run an Autotask sync to populate `backup_type_udf` values
9. Implement `veeam-compliance-service.ts` (depends on both Veeam and Autotask data being synced)
10. Build the Contract Compliance UI tab

### Testing Checklist
- [ ] Verify API key authentication works
- [ ] Test organization fetch and company matching
- [ ] Test full sync of all entities
- [ ] Test incremental sync (only changed records)
- [ ] Test with VSPC API unavailable (graceful failure)
- [ ] Test pagination with large datasets (100+ workloads)
- [ ] Verify Backup Status page renders correctly with real data
- [ ] Verify Backup Status page renders gracefully with no data
- [ ] Test company filter and status filter on Backup Status page
- [ ] Check database migration runs cleanly on existing schema
- [ ] Verify sync scheduler runs Veeam sync on configured schedule
- [ ] Verify `backup_type_udf` is populated after Autotask sync
- [ ] Test compliance computation with known contracted devices
- [ ] Verify "contracted but not backed up" mismatches are correctly identified
- [ ] Verify "backed up but not contracted" mismatches are correctly identified
- [ ] Test compliance UI tab renders correctly with mismatch data
- [ ] Test compliance UI with zero mismatches (all green)
- [ ] Verify compliance recomputes after each Veeam sync

### Code Review Focus Areas
- Error handling completeness (VSPC API can be unreliable)
- TypeScript type safety for all VSPC API responses
- Consistent code style with existing services
- Proper logging for debugging sync issues
- Performance (batch inserts, avoid N+1 queries)
- Database index usage for Backup Status page queries
