2.7 KiB
2.7 KiB
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
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/Agracefully. invoiceToEmailAddresson the company record is often billing/AP, not the operational contact.