# ConnectWise Customer Aggregation Recipe Reference: How to aggregate open tickets by customer and enrich with primary contact details. ## Use Case - "List customers who have had open tickets in the past N years" - "Show me every company with active Help Desk tickets and their primary contact email" ## API Traversal ```python import base64 import requests from datetime import datetime, timedelta # Auth creds = {...} # from ~/.secrets/connectwise auth_str = f"{company_id}{public_key}:{private_key}" encoded_auth = base64.b64encode(auth_str.encode()).decode() headers = { 'Authorization': f'Basic {encoded_auth}', 'clientId': client_id, 'Company-Id': company_id, 'Content-Type': 'application/json' } # 1. Fetch tickets base_url = 'https://cw.vistanetinc.com/v4_6_release/apis/3.0' two_years_ago = (datetime.now() - timedelta(days=730)).strftime('%Y-%m-%dT%H:%M:%SZ') conditions = f"board/id=1 and closedFlag=false and dateEntered>'{two_years_ago}'" url = f"{base_url}/service/tickets?conditions={conditions}&pageSize=1000" tickets = requests.get(url, headers=headers).json() # 2. Aggregate by company companies = {} for t in tickets: status_name = t.get('status', {}).get('name', '') if status_name.startswith('>'): continue comp_id = t.get('company', {}).get('id') if not comp_id: continue companies.setdefault(comp_id, { 'name': t['company']['name'], 'ticket_count': 0, 'latest_date': t['_info']['dateEntered'] }) companies[comp_id]['ticket_count'] += 1 if t['_info']['dateEntered'] > companies[comp_id]['latest_date']: companies[comp_id]['latest_date'] = t['_info']['dateEntered'] # 3. Enrich with primary contact email for comp_id, data in companies.items(): comp = requests.get(f"{base_url}/company/companies/{comp_id}", headers=headers).json() contact_id = comp.get('defaultContact', {}).get('id') data['contact_name'] = comp.get('defaultContact', {}).get('name', 'N/A') data['email'] = comp.get('invoiceToEmailAddress', 'N/A') if contact_id: contact = requests.get(f"{base_url}/company/contacts/{contact_id}", headers=headers).json() for item in contact.get('communicationItems', []): if item.get('type', {}).get('name') == 'Email' and item.get('defaultFlag'): data['email'] = item['value'] break ``` ## Board IDs - VistaNet Help Desk = 1 - VistaNet Management = 25 - Managed Service Alerts = 6 ## Known Gaps - Some companies (e.g., BJ LaRossa, Far West Rice Inc.) have no default contact email in CW. Display `N/A` gracefully. - `invoiceToEmailAddress` on the company record is often billing/AP, not the operational contact.