# Product Requirements Document: Teams Phone CLI

**Version:** 1.0
**Date:** 2026-01-27
**Status:** Draft
**Author:** Product Team

---

## 1. Executive Summary

The Teams Phone CLI is a command-line tool that enables IT administrators and Managed Service Providers (MSPs) to manage Microsoft Teams Phone configurations efficiently through scriptable commands, eliminating the need for repetitive GUI interactions in Teams Admin Center. By providing automation capabilities for user management, phone number assignments, policy management, and multi-tenant operations, this tool reduces time spent on routine phone administration tasks by 50% or more while enabling workflows that are impossible through the web interface alone.

---

## 2. Problem Statement

### Current Situation

IT administrators and MSPs managing Microsoft Teams Phone deployments face significant operational challenges:

1. **Manual, Repetitive Work**: The Teams Admin Center (TAC) requires clicking through multiple screens for each phone number assignment, policy change, or user configuration. Bulk operations are tedious and error-prone.

2. **No Scripting/Automation**: While PowerShell exists for Teams management, it requires the MicrosoftTeams module, Windows-focused tooling, and lacks a modern CLI experience. There is no simple command-line tool for quick lookups or automated workflows.

3. **Multi-Tenant Complexity**: MSPs managing multiple client tenants must log in/out repeatedly, losing context and efficiency when performing the same operations across clients.

### User Impact

| User Type | Pain Point | Impact |
|-----------|------------|--------|
| IT Administrators | 15-30 minutes per bulk phone assignment | Lost productivity, delayed onboarding |
| MSPs/Partners | Context-switching between tenants | Billing inefficiency, increased error rate |
| Help Desk Staff | Cannot quickly look up user phone configs | Longer ticket resolution times |

### Business Impact

- **Operational Cost**: Estimated 10+ hours/week spent on routine phone management tasks per IT admin
- **Onboarding Delays**: New employee phone setup takes 2-3x longer than necessary
- **Error Rate**: Manual bulk operations lead to configuration mistakes requiring rework
- **MSP Profitability**: Time spent on repetitive tasks reduces margin on managed services contracts

### Why Solve This Now

- Microsoft Graph API beta endpoints for Teams Phone management became available in late 2025
- Increasing Teams Phone adoption (Microsoft Calling Plans, Direct Routing, Operator Connect)
- Customer demand for automation tooling to compete with legacy PBX management tools
- MSP market growth requires efficient multi-tenant management

---

## 3. Goals & Success Metrics

### Primary Goals

| Goal ID | Goal | Metric | Baseline | Target | Timeframe |
|---------|------|--------|----------|--------|-----------|
| G-001 | Reduce phone management task time | Time to complete common workflows | 15-30 min (GUI) | < 5 min (CLI) | MVP Launch |
| G-002 | Enable automation workflows | % of common tasks scriptable | 0% (no CLI exists) | 90%+ of MVP commands | MVP Launch |
| G-003 | Support multi-tenant operations | Tenant switch time | 2-3 min (re-login) | < 5 seconds | MVP Launch |
| G-004 | Achieve reliable bulk operations | Bulk assign success rate | N/A | > 95% success rate | MVP + 1 month |
| G-005 | Provide actionable error feedback | % errors with remediation guidance | N/A | 100% of error types | MVP Launch |

### Secondary Goals

| Goal ID | Goal | Metric | Target | Timeframe |
|---------|------|--------|--------|-----------|
| G-006 | Cross-platform support | Platforms supported | Windows, macOS, Linux | MVP Launch |
| G-007 | Machine-readable output | Commands with JSON output | 100% of list/show commands | MVP Launch |
| G-008 | Secure credential handling | Plaintext secrets in config | 0 (never stored) | MVP Launch |

---

## 4. User Stories

### US-001: Quick User Lookup

**As an** IT administrator
**I want to** quickly look up a user's phone configuration from the command line
**So that I can** answer help desk tickets without navigating through the Teams Admin Center

**Acceptance Criteria:**
1. Can retrieve user by UPN, display name, or object ID
2. Response displays phone number, license status, and all voice policies
3. Command completes in < 3 seconds for single user lookup
4. Output clearly shows policy assignment source (direct/group/global)

**Implementation Tasks:**
- Implement `users show <user>` command
- Add user resolution logic (UPN/name/ID)
- Format policy display with assignment source

---

### US-002: Assign Phone Number

**As an** IT administrator
**I want to** assign a phone number to a user with emergency location in one command
**So that I can** complete new employee phone setup in under a minute

**Acceptance Criteria:**
1. Can specify user, phone number, and emergency location in single command
2. Validates user has Teams Phone license before attempting assignment
3. Validates location exists (from CSV cache) before API call
4. Shows real-time progress while async operation completes
5. Provides clear success/failure message with details

**Implementation Tasks:**
- Implement `numbers assign <user> <number> [location]` command
- Add async operation polling with progress display
- Implement location validation against CSV cache
- Add E911 enforcement for Calling Plan numbers

---

### US-003: Bulk Phone Assignment

**As an** MSP administrator
**I want to** assign phone numbers to multiple users from a CSV file
**So that I can** onboard entire departments or client organizations efficiently

**Acceptance Criteria:**
1. Accepts CSV with columns: user, phone_number, location
2. Validates all rows before starting assignments (--dry-run mode)
3. Shows live progress bar during execution
4. Continues processing after individual failures (--continue-on-error)
5. Generates results file with success/failure per row
6. Supports default location fallback for rows without location

**Implementation Tasks:**
- Implement `numbers bulk-assign <csv-file>` command
- Add CSV validation and parsing
- Implement progress bar with real-time status
- Generate results output file

---

### US-004: Search Phone Inventory

**As an** IT administrator
**I want to** search available phone numbers by pattern or status
**So that I can** find an appropriate number to assign to a new user

**Acceptance Criteria:**
1. Can search by last 4 digits, area code pattern, or wildcard
2. Can filter by status (unassigned, assigned, reserved)
3. Can filter by number type (Calling Plan, Direct Routing, Operator Connect)
4. Results show number, status, type, and assigned user (if any)

**Implementation Tasks:**
- Implement `numbers search <pattern>` command
- Add pattern matching (wildcards, partial numbers)
- Add status and type filters

---

### US-005: Multi-Tenant Management

**As an** MSP administrator
**I want to** quickly switch between client tenants
**So that I can** perform operations across multiple clients without re-authenticating

**Acceptance Criteria:**
1. Can list all configured tenant profiles
2. Can switch active tenant with single command (< 5 seconds)
3. Cached tokens are reused when switching back to a tenant
4. Can override tenant for single command with --tenant flag
5. Clear indication of current active tenant in prompts/output

**Implementation Tasks:**
- Implement `tenants list`, `tenants switch`, `tenants current` commands
- Implement token caching per tenant
- Add --tenant global flag override

---

### US-006: Policy Assignment

**As an** IT administrator
**I want to** view and assign voice policies to users
**So that I can** manage calling, voicemail, and emergency policies consistently

**Acceptance Criteria:**
1. Can view all policy assignments for a user with source (direct/group/global)
2. Can assign any supported voice policy type to a user
3. Can reset user to global/default policy
4. Validates policy exists before assignment attempt

**Implementation Tasks:**
- Implement `policies show <user>` command
- Implement `policies assign <user> <type> <name>` command
- Add policy validation against CSV cache

---

### US-007: Emergency Location Management

**As an** IT administrator
**I want to** view and search emergency locations
**So that I can** select the correct location when assigning phone numbers

**Acceptance Criteria:**
1. Can list all emergency locations with address details
2. Can search locations by name, city, or address
3. Can view full details of a specific location
4. Shows staleness warning if CSV data is > 30 days old

**Implementation Tasks:**
- Implement `locations list`, `locations show`, `locations search` commands
- Add CSV reading and validation
- Implement staleness detection and warnings

---

### US-008: Unassign Phone Number

**As an** IT administrator
**I want to** unassign a phone number from a user
**So that I can** reclaim numbers from departing employees

**Acceptance Criteria:**
1. Can unassign by specifying only the phone number
2. Shows confirmation prompt before unassignment (unless --force)
3. Provides dry-run preview option
4. Waits for async operation completion by default

**Implementation Tasks:**
- Implement `numbers unassign <number>` command
- Add confirmation prompt and --force flag
- Add --dry-run support

---

## 5. Functional Requirements

### User Management

| ID | Requirement | Priority | Testable Criteria |
|----|-------------|----------|-------------------|
| REQ-001 | List users with voice configuration including license status, phone number, and voice-enabled status | Must Have | Given authenticated CLI, when `users list` executed, then returns users with all specified fields |
| REQ-002 | Filter users by licensed/unlicensed, assigned/unassigned, and account type | Must Have | Given `users list --licensed --unassigned`, then only licensed users without phone numbers returned |
| REQ-003 | Show detailed user configuration including all voice policies and assignment sources | Must Have | Given valid user identifier, when `users show <user>` executed, then returns all voice policies with source |
| REQ-004 | Search users by name, UPN, or phone number with partial matching | Must Have | Given search query "john", when `users search john` executed, then returns all matching users |
| REQ-005 | Resolve users by UPN, display name, or object ID with disambiguation for multiple matches | Must Have | Given ambiguous display name, when used in command, then error lists matching candidates |

### Number Management

| ID | Requirement | Priority | Testable Criteria |
|----|-------------|----------|-------------------|
| REQ-006 | List all phone numbers with status, type, assignment, and location | Must Have | Given authenticated CLI, when `numbers list` executed, then returns all numbers with specified fields |
| REQ-007 | Filter numbers by status (assigned/unassigned), type, and city | Must Have | Given filters applied, when `numbers list --status unassigned --type calling-plan`, then only matching numbers returned |
| REQ-008 | Show detailed number information including activation state, port status, and capabilities | Must Have | Given valid phone number, when `numbers show <number>` executed, then returns all detail fields |
| REQ-009 | Search numbers by pattern with wildcard support (last 4 digits, area code) | Must Have | Given pattern "206*", when `numbers search "206*"` executed, then returns all 206 area code numbers |
| REQ-010 | Assign phone number to user with emergency location, supporting async polling | Must Have | Given valid user/number/location, when `numbers assign` executed, then number assigned and status confirmed |
| REQ-011 | Enforce emergency location requirement for Calling Plan numbers (E911 compliance) | Must Have | Given Calling Plan number without location, when assign attempted, then error with location requirement message |
| REQ-012 | Unassign phone number with confirmation prompt and async polling | Must Have | Given assigned number, when `numbers unassign` executed with confirmation, then number unassigned |
| REQ-013 | Update phone number location or network site without reassignment | Should Have | Given assigned number, when `numbers update --location` executed, then location updated synchronously |
| REQ-014 | Bulk assign numbers from CSV with progress reporting and error handling | Must Have | Given valid CSV file, when `numbers bulk-assign` executed, then all rows processed with progress shown |
| REQ-015 | Support dry-run mode for assign/unassign/bulk operations | Must Have | Given --dry-run flag, when operation executed, then preview shown without changes made |

### Policy Management

| ID | Requirement | Priority | Testable Criteria |
|----|-------------|----------|-------------------|
| REQ-016 | List available policies by type from CSV cache | Must Have | Given valid CSV cache, when `policies list calling` executed, then all calling policies listed |
| REQ-017 | Show user's effective policy assignments with assignment source | Must Have | Given valid user, when `policies show <user>` executed, then all policies shown with direct/group/global source |
| REQ-018 | Assign policy to user with validation against known policies | Must Have | Given valid policy name, when `policies assign` executed, then policy assigned successfully |
| REQ-019 | Support all voice policy types: calling, voicemail, emergency, caller-id, dial-plan, voice-routing | Must Have | Given any supported policy type, when policy commands executed, then operation succeeds |
| REQ-020 | Bulk assign policies from CSV | Should Have | Given valid CSV file, when `policies bulk-assign` executed, then all rows processed |

### Location Management

| ID | Requirement | Priority | Testable Criteria |
|----|-------------|----------|-------------------|
| REQ-021 | List emergency locations from CSV cache with address details | Must Have | Given valid CSV cache, when `locations list` executed, then all locations listed with addresses |
| REQ-022 | Search locations by name, city, or address | Must Have | Given search query, when `locations search` executed, then matching locations returned |
| REQ-023 | Show detailed location information | Must Have | Given valid location identifier, when `locations show` executed, then full details displayed |
| REQ-024 | Display staleness warning when CSV cache > 30 days old | Must Have | Given stale CSV, when location commands executed, then warning displayed with refresh guidance |
| REQ-025 | Operate in degraded mode when CSV files missing | Should Have | Given missing CSV, when commands executed, then warning shown and operation proceeds where possible |

### Tenant Management

| ID | Requirement | Priority | Testable Criteria |
|----|-------------|----------|-------------------|
| REQ-026 | List all configured tenant profiles | Must Have | Given configured tenants, when `tenants list` executed, then all profiles listed with status |
| REQ-027 | Switch active tenant preserving cached tokens | Must Have | Given valid tenant name, when `tenants switch` executed, then context switched in < 5 seconds |
| REQ-028 | Add new tenant profile via interactive wizard or flags | Must Have | Given valid credentials, when `tenants add` executed, then profile saved to config |
| REQ-029 | Test tenant connection and permissions | Must Have | Given tenant profile, when `tenants test` executed, then connectivity and permissions validated |
| REQ-030 | Remove tenant profile with confirmation | Must Have | Given existing tenant, when `tenants remove` executed with confirmation, then profile deleted |

### Authentication

| ID | Requirement | Priority | Testable Criteria |
|----|-------------|----------|-------------------|
| REQ-031 | Support certificate-based authentication (recommended) | Must Have | Given valid certificate, when `auth login --method certificate` executed, then authenticated successfully |
| REQ-032 | Support client-secret authentication with env var | Must Have | Given secret in env var, when `auth login --method client-secret` executed, then authenticated successfully |
| REQ-033 | Auto-authenticate when token missing or expired | Must Have | Given expired token, when any command executed, then auto-login initiated |
| REQ-034 | Show current authentication status | Must Have | Given authenticated session, when `auth status` executed, then shows tenant, expiry, method |
| REQ-035 | Clear cached tokens (single tenant or all) | Must Have | Given cached tokens, when `auth logout` executed, then tokens cleared |
| REQ-036 | Refresh authentication tokens | Should Have | Given valid session, when `auth refresh` executed, then token refreshed |

### Output & UX

| ID | Requirement | Priority | Testable Criteria |
|----|-------------|----------|-------------------|
| REQ-037 | Support JSON output for all commands via --json flag | Must Have | Given any list/show command, when --json flag used, then valid JSON output returned |
| REQ-038 | Display rich table output with color-coded status by default | Must Have | Given terminal with color support, when command executed, then colored tables displayed |
| REQ-039 | Show progress bars for bulk operations | Must Have | Given bulk operation, when executing, then progress bar shows completion percentage |
| REQ-040 | Provide verbose mode with API call logging | Must Have | Given --verbose flag, when command executed, then API calls and timing shown |
| REQ-041 | Include remediation guidance in all error messages | Must Have | Given any error, when displayed, then includes suggestions for resolution |

### API Contract Validation

| ID | Requirement | Priority | Testable Criteria |
|----|-------------|----------|-------------------|
| REQ-042 | Validate API responses against strict schemas, failing fast on unexpected changes | Must Have | Given schema mismatch, when API response received, then operation fails with ContractError |
| REQ-043 | Provide `api-check` command to validate API contracts before operations | Must Have | Given test tenant, when `api-check` executed, then all endpoint schemas validated and report generated |

---

## 6. Non-Functional Requirements

### Performance

| Requirement | Target | Measurement |
|-------------|--------|-------------|
| Single user/number lookup | < 3 seconds | p95 latency |
| List commands (default page) | < 5 seconds | p95 latency |
| Tenant switch | < 5 seconds | p95 latency |
| Bulk operation throughput | > 10 items/minute | Sustained rate accounting for API throttling |
| CLI startup time | < 500ms | Cold start to ready |

### Security

| Requirement | Implementation |
|-------------|----------------|
| No plaintext secrets in config files | Secrets via environment variables or secure storage only |
| Certificate-based auth preferred | Documentation recommends certificates over client secrets |
| Token caching with encryption | Cached tokens encrypted at rest |
| Audit logging capability | All operations logged with sanitized PII (--debug-log) |
| Secrets never in debug logs | Tokens and secrets masked in all log output |

### Reliability

| Requirement | Target |
|-------------|--------|
| Graceful API error handling | 100% of Graph API errors mapped to user-friendly messages |
| Retry with backoff for transient failures | 3 retries with exponential backoff for 429/503 |
| Cache fallback for read operations | Stale data served with warning if API unavailable |
| Bulk operation continuation | --continue-on-error allows processing to complete |
| Schema validation | 100% of Graph API responses validated against Pydantic models (strict mode) |
| Contract drift detection | Automated CI check runs daily against test tenant |

### Scalability

| Dimension | Requirement |
|-----------|-------------|
| User count | Support tenants with up to 5,000 users |
| Number inventory | Support up to 10,000 phone numbers per tenant |
| Bulk operations | Handle CSV files with up to 1,000 rows |
| Concurrent tenants | Support 50+ configured tenant profiles |

### Compatibility

| Dimension | Requirement |
|-----------|-------------|
| Operating Systems | Windows 10+, macOS 12+, Linux (Ubuntu 20.04+, RHEL 8+) |
| Python Version | Python 3.10+ |
| Terminal | Support for standard terminals, CI/CD environments |
| Graph API | Beta endpoints (with awareness of potential changes) |

### Accessibility

| Requirement | Implementation |
|-------------|----------------|
| Screen reader compatible | Text-based output, no ASCII art dependencies |
| Color optional | --no-color flag or NOCOLOR env var support |
| Keyboard only operation | No mouse/GUI dependencies |

---

## 7. Technical Considerations

### Architecture Overview

```
┌─────────────────────────────────────────────────────────────────┐
│                        Teams Phone CLI                          │
│  (Python + Typer)                                               │
├─────────────────────────────────────────────────────────────────┤
│  CLI Layer (Typer)                                              │
│  ├── users commands                                             │
│  ├── numbers commands                                           │
│  ├── policies commands                                          │
│  ├── locations commands                                         │
│  ├── tenants commands                                           │
│  └── auth commands                                              │
├─────────────────────────────────────────────────────────────────┤
│  Service Layer                                                  │
│  ├── UserService        (Graph API)                             │
│  ├── NumberService      (Graph API + async polling)             │
│  ├── PolicyService      (Graph API + CSV)                       │
│  ├── LocationService    (CSV only)                              │
│  ├── TenantService      (Local config)                          │
│  └── AuthService        (MSAL)                                  │
├─────────────────────────────────────────────────────────────────┤
│  Infrastructure Layer                                           │
│  ├── GraphClient        (HTTP client, retry, throttling)        │
│  ├── CacheManager       (Token cache, data cache, CSV cache)    │
│  ├── ConfigManager      (TOML config, tenant profiles)          │
│  └── OutputFormatter    (Tables, JSON, progress bars)           │
└─────────────────────────────────────────────────────────────────┘
            │                                    │
            ▼                                    ▼
┌──────────────────────────┐      ┌──────────────────────────────┐
│   Microsoft Graph API    │      │   Local CSV Cache            │
│   (Beta endpoints)       │      │   ~/.teams-phone/cache/      │
│   - /admin/teams/*       │      │   ├── locations.csv          │
│                          │      │   ├── policies.csv           │
│                          │      │   └── export-metadata.json   │
└──────────────────────────┘      └──────────────────────────────┘
```

### API Specifications

#### Graph API Endpoints (Beta)

| Operation | Endpoint | Method |
|-----------|----------|--------|
| List user configs | `/admin/teams/userConfigurations` | GET |
| Get user config | `/admin/teams/userConfigurations/{id}` | GET |
| List numbers | `/admin/teams/telephoneNumberManagement/numberAssignments` | GET |
| Assign number | `/admin/teams/telephoneNumberManagement/numberAssignments/assignNumber` | POST |
| Unassign number | `/admin/teams/telephoneNumberManagement/numberAssignments/unassignNumber` | POST |
| Update number | `/admin/teams/telephoneNumberManagement/numberAssignments/updateNumber` | POST |
| Poll operation | `/admin/teams/telephoneNumberManagement/operations/{id}` | GET |
| Assign policy | `/admin/teams/policy/userAssignments/assign` | POST |

#### Phone Number Format Normalization

```python
# Input: E.164 format (+12065551234) or digits (12065551234)
# assignNumber/unassignNumber: digits only (12065551234)
# updateNumber: E.164 with + prefix (+12065551234)

def normalize_for_assign(number: str) -> str:
    return number.lstrip('+')

def normalize_for_update(number: str) -> str:
    if not number.startswith('+'):
        return f'+{number}'
    return number
```

#### Async Operation Polling

```python
# POST to assignNumber/unassignNumber returns 202 Accepted
# Location header: .../operations('{operation_id}')

async def poll_operation(operation_id: str, timeout: int = 60) -> OperationResult:
    start = time.time()
    while time.time() - start < timeout:
        result = await graph_client.get(f'/admin/teams/telephoneNumberManagement/operations/{operation_id}')
        if result['status'] in ('succeeded', 'failed', 'skipped'):
            return result
        await asyncio.sleep(2 + random.uniform(0, 1))  # 2-3 second intervals
    raise TimeoutError(f"Operation {operation_id} did not complete within {timeout}s")
```

### Configuration Schema

```toml
# ~/.teams-phone/config.toml

[default]
tenant = "contoso"
output_format = "table"
color = true

[cache]
users_ttl = 300      # 5 minutes
numbers_ttl = 300    # 5 minutes
policies_ttl = 600   # 10 minutes

[tenants.contoso]
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
display_name = "Contoso Ltd"
auth_method = "certificate"
cert_path = "/path/to/contoso-cert.pem"
default_location = "Seattle-HQ"

[tenants.fabrikam]
tenant_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
client_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
display_name = "Fabrikam Inc"
auth_method = "client-secret"
# secret via TEAMS_PHONE_CLIENT_SECRET env var
```

### CSV Cache Schemas

**locations.csv:**
```csv
locationId,civicAddressId,description,companyName,address,city,stateOrProvince,postalCode,countryOrRegion,isDefault
93cb8a70-...,a1b2c3d4-...,Main Office - Floor 3,Contoso Ltd,123 Main St,Seattle,WA,98101,US,true
```

**policies.csv:**
```csv
policyType,policyName,policyId,description,isGlobal
TeamsCallingPolicy,Global,Global,Default calling policy,true
TeamsCallingPolicy,AllowCalling,AllowCalling,Allows all calling features,false
```

### Dependencies

| Dependency | Purpose | Version |
|------------|---------|---------|
| typer | CLI framework | ^0.9.0 |
| httpx | Async HTTP client | ^0.25.0 |
| msal | Microsoft authentication | ^1.24.0 |
| rich | Terminal formatting | ^13.0.0 |
| pydantic | Data validation | ^2.0.0 |
| toml | Config file parsing | ^0.10.0 |

### External Dependencies

| System | Requirement | Purpose |
|--------|-------------|---------|
| Microsoft Entra ID | App Registration | Service principal for Graph API access |
| Microsoft Graph API | Beta endpoints | Teams Phone management operations |
| PowerShell + MicrosoftTeams module | Optional | CSV export for locations and policies |

### Testing Strategy

| Layer | Approach | Coverage Target |
|-------|----------|-----------------|
| Unit Tests | pytest, mock Graph responses | > 80% |
| Integration Tests | Test tenant with sample data | Core workflows |
| Contract Tests | Schema snapshots validated against live API (daily CI) | All Graph endpoints |
| E2E Tests | Real API calls (separate test tenant) | Smoke tests |
| Load Tests | Bulk operations with 1000 rows | Performance validation |

---

## 8. Implementation Roadmap

### Phase 1: Foundation (Core Infrastructure)

**Objective:** Establish CLI framework, authentication, and basic tenant management

| Task | Requirements | Complexity |
|------|--------------|------------|
| Set up Python project structure with Typer | - | Low |
| Implement ConfigManager (TOML parsing) | REQ-026-030 | Medium |
| Implement AuthService (MSAL integration) | REQ-031-036 | High |
| Implement token caching and encryption | REQ-031-036 | Medium |
| Add `auth` command group (login, status, logout) | REQ-031-035 | Medium |
| Add `tenants` command group (list, current, switch, add, remove, test) | REQ-026-030 | Medium |
| Implement GraphClient with retry/throttling | - | High |
| Implement strict Pydantic models for all API responses | REQ-042 | Medium |
| Add `api-check` command for contract validation | REQ-043 | Medium |
| Add global flags (--json, --verbose, --tenant) | REQ-037, REQ-040 | Low |
| **Unit tests**: ConfigManager, AuthService, GraphClient (retry/backoff logic) | - | Medium |
| **Integration tests**: Auth flows against test tenant, token caching | - | Medium |
| Set up pytest fixtures and test tenant configuration | - | Low |

### Phase 2: Read Operations (Users, Numbers, Locations)

**Objective:** Enable all read/query operations

| Task | Requirements | Complexity |
|------|--------------|------------|
| Implement UserService with Graph API integration | REQ-001-005 | Medium |
| Add `users` commands (list, show, search) | REQ-001-005 | Medium |
| Implement NumberService with Graph API integration | REQ-006-009 | Medium |
| Add `numbers` commands (list, show, search) | REQ-006-009 | Medium |
| Implement LocationService (CSV reading) | REQ-021-025 | Medium |
| Add `locations` commands (list, show, search) | REQ-021-025 | Low |
| Implement CSV staleness detection | REQ-024 | Low |
| Add rich table output formatting | REQ-038 | Medium |
| **Unit tests**: UserService, NumberService, LocationService (mock Graph responses) | - | Medium |
| **Unit tests**: CSV parsing, staleness detection, user resolution logic | - | Low |
| **Integration tests**: Read operations against test tenant with sample data | - | Medium |
| **Contract tests**: Validate Pydantic models against live API responses | - | Medium |

### Phase 3: Write Operations (Assign, Unassign, Policies)

**Objective:** Enable all modification operations

| Task | Requirements | Complexity |
|------|--------------|------------|
| Implement async operation polling | REQ-010, REQ-012 | High |
| Add `numbers assign` command with E911 enforcement | REQ-010, REQ-011 | High |
| Add `numbers unassign` command | REQ-012 | Medium |
| Add `numbers update` command | REQ-013 | Medium |
| Implement PolicyService | REQ-016-019 | Medium |
| Add `policies` commands (list, show, assign) | REQ-016-019 | Medium |
| Add confirmation prompts and --force flag | REQ-012, REQ-015 | Low |
| Add --dry-run support | REQ-015 | Medium |
| **Unit tests**: Async polling logic, E911 validation, PolicyService | - | Medium |
| **Unit tests**: Dry-run mode, confirmation prompts | - | Low |
| **Integration tests**: Assign/unassign workflows against test tenant | - | High |
| **Integration tests**: Policy assignment, E911 enforcement scenarios | - | Medium |

### Phase 4: Bulk Operations & Polish

**Objective:** Enable bulk operations and production-ready UX

| Task | Requirements | Complexity |
|------|--------------|------------|
| Implement CSV parsing for bulk operations | REQ-014 | Medium |
| Add `numbers bulk-assign` command | REQ-014 | High |
| Add `policies bulk-assign` command | REQ-020 | Medium |
| Implement progress bars | REQ-039 | Medium |
| Add --continue-on-error handling | REQ-014 | Medium |
| Implement results file output | REQ-014 | Low |
| Add debug logging (--debug-log) | REQ-040 | Medium |
| Error message polish and remediation guidance | REQ-041 | Medium |
| **Unit tests**: CSV parsing edge cases, error handling, results file generation | - | Medium |
| **Unit tests**: Progress bar updates, continue-on-error logic | - | Low |
| **Integration tests**: Bulk assign with 100-row CSV against test tenant | - | High |
| **Load tests**: Bulk operations with 1000 rows, validate performance targets | - | Medium |

### Phase 5: Documentation & Release Prep

**Objective:** Production readiness and documentation

| Task | Requirements | Complexity |
|------|--------------|------------|
| Coverage gap analysis and test hardening | - | Medium |
| Create user documentation (CLI reference, tutorials) | - | Medium |
| Create PowerShell export script with documentation | - | Medium |
| Set up daily CI contract tests against test tenant | - | Medium |
| Performance profiling and optimization pass | - | Medium |
| Release packaging (PyPI, standalone binary evaluation) | - | Medium |

---

## 9. Out of Scope

The following items are explicitly **NOT included** in this MVP:

### Functionality Not Included

| Item | Reason | Future Consideration |
|------|--------|----------------------|
| Number reservation/unreservation | No Graph API or PowerShell equivalent exists | Monitor for future API availability |
| Location creation/modification | Graph API does not support; requires TAC or PowerShell | May add PowerShell wrapper post-MVP |
| Policy creation/modification | Graph API does not support listing/creating policies | May add PowerShell wrapper post-MVP |
| Resource account management | Complex scenario, separate domain | Phase 2 feature |
| Call queue/auto-attendant management | Different API surface, complex workflows | Phase 2 feature |
| Teams meeting policies | Not voice-specific | Out of domain |
| Reporting/analytics | Requires different data sources | Phase 2 feature |
| GUI/TUI interface | CLI-first approach | Community contribution opportunity |

### Technical Scope Exclusions

| Item | Reason |
|------|--------|
| Graph API v1.0 | Teams Admin APIs are beta-only |
| Interactive/device-code auth | Service principal only (automation focus) |
| Windows certificate store | File-based certificates for cross-platform |
| Automatic CSV refresh | User runs PowerShell script manually |
| Real-time webhooks | Polling-based approach for MVP |

---

## 10. Open Questions & Risks

### Open Questions

| ID | Question | Owner | Status | Decision |
|----|----------|-------|--------|----------|
| OQ-001 | Should we support interactive device-code auth for personal use? | Product | Open | Leaning no - automation focus |
| OQ-002 | Package distribution: PyPI, standalone binary, or both? | Engineering | Open | - |
| OQ-003 | Should CSV export be bundled or separate repo? | Engineering | Open | - |
| OQ-004 | What is the minimum supported Python version? | Engineering | Open | Likely 3.10+ |

### Known Risks

| ID | Risk | Probability | Impact | Mitigation |
|----|------|-------------|--------|------------|
| R-001 | Microsoft Graph beta APIs change without notice | Medium | High | Strict Pydantic schema validation (extra='forbid'), `api-check` pre-flight command, daily CI contract tests against test tenant, immediate failure on schema mismatch |
| R-002 | PowerShell Teams module incompatibilities | Medium | Medium | Document supported versions, test matrix |
| R-003 | Rate limiting impacts bulk operations | High | Medium | Implement smart throttling, batch where possible, document limits |
| R-004 | TeamsVoicemailPolicy cannot be assigned via Graph | Confirmed | Low | Document limitation, suggest PowerShell fallback |
| R-005 | Large tenant performance (> 5000 users) | Low | Medium | Implement pagination, streaming output |
| R-006 | Certificate management complexity | Medium | Medium | Provide clear documentation, support multiple cert formats |

### Areas Needing Research

| Topic | Questions | Priority |
|-------|-----------|----------|
| Token encryption | Best practice for cross-platform secure token storage | High |
| Batch API optimization | Can batch endpoint improve bulk operation performance? | Medium |
| Shell completion | Best approach for bash/zsh/fish/PowerShell completions | Low |

---

## 11. Validation Checkpoints

### Checkpoint 1: Authentication & Tenant Management (Phase 1 Complete)

**Validation Criteria:**
- [ ] Can authenticate with certificate
- [ ] Can authenticate with client secret (env var)
- [ ] Can add/list/switch/remove tenant profiles
- [ ] Token caching works across sessions
- [ ] --tenant flag overrides active tenant
- [ ] `api-check` validates all endpoint schemas and reports results
- [ ] API responses fail fast on unexpected schema changes (strict Pydantic)

### Checkpoint 2: Read Operations (Phase 2 Complete)

**Validation Criteria:**
- [ ] `users list` returns paginated results with all fields
- [ ] `users show` returns complete voice configuration
- [ ] `numbers list` returns inventory with status
- [ ] `numbers search` finds numbers by pattern
- [ ] `locations list` reads from CSV correctly
- [ ] Staleness warning displayed for old CSV
- [ ] --json output is valid JSON for all commands

### Checkpoint 3: Write Operations (Phase 3 Complete)

**Validation Criteria:**
- [ ] `numbers assign` completes async operation successfully
- [ ] E911 enforcement blocks Calling Plan without location
- [ ] `numbers unassign` completes successfully
- [ ] `policies assign` applies policy correctly
- [ ] --dry-run shows preview without changes
- [ ] --force skips confirmation prompts

### Checkpoint 4: Bulk Operations (Phase 4 Complete)

**Validation Criteria:**
- [ ] `numbers bulk-assign` processes 100-row CSV successfully
- [ ] Progress bar updates in real-time
- [ ] --continue-on-error processes all rows
- [ ] Results file generated with success/failure status
- [ ] Error messages include remediation guidance

### Checkpoint 5: Production Ready (Phase 5 Complete)

**Validation Criteria:**
- [ ] Unit test coverage > 80%
- [ ] All core workflows pass integration tests
- [ ] Documentation complete
- [ ] PowerShell export script tested
- [ ] Performance meets targets (< 3s single lookup, < 5s list)

---

## Appendix A: Exit Code Reference

| Code | Meaning | Example Scenario |
|------|---------|------------------|
| 0 | Success | Command completed successfully |
| 1 | General error | Unexpected error, network failure |
| 2 | Authentication error | Invalid credentials, expired token |
| 3 | Authorization error | Missing Graph API permissions |
| 4 | Not found | User, number, or policy not found |
| 5 | Validation error | Invalid input, missing required field, E911 violation |
| 6 | Rate limit exceeded | Graph API throttling after retries |
| 7 | Configuration error | Invalid config file, missing tenant |

---

## Appendix B: Required Graph API Permissions

| Permission | Type | Purpose |
|------------|------|---------|
| `TeamsUserConfiguration.Read.All` | Application | Read user voice configuration |
| `TeamsTelephoneNumber.Read.All` | Application | Read phone number inventory |
| `TeamsTelephoneNumber.ReadWrite.All` | Application | Assign/unassign/update numbers |
| `TeamsPolicyUserAssign.ReadWrite.All` | Application | Assign policies to users |
| `User.Read.All` | Application | Read user profiles |
| `Directory.Read.All` | Application | Read tenant information |

---

*Document generated by PRD Generator skill*
