Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions SETUP_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# ConnectWise Manage API Setup Guide

Based on your ConnectWise instance, here's your setup information:

## Your ConnectWise Instance Details

- **UI Host**: `na.myconnectwise.net`
- **API Host**: `api-na.myconnectwise.net` (typical API endpoint)
- **Codebase**: `v2025_1` (from your URL)

## Step 1: Install the Repository

### Option A: Poetry (Recommended)
```bash
cd /Users/samstacks/.cursor/worktrees/pyconnectwise/yqh
poetry install --with dev
poetry run python -c "import pyconnectwise; print('ok')"
```

### Option B: venv + editable install
```bash
cd /Users/samstacks/.cursor/worktrees/pyconnectwise/yqh
python3 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .
python -c "import pyconnectwise; print('ok')"
```

## Step 2: Gather Your Credentials

You need **5 pieces of information**:

1. **CW_COMPANY**: Your ConnectWise login "Company" name
- This is the value you type in the Company field on the login screen
- You can try fetching it: `python fetch_company_info.py` (set CW_COMPANY first)

2. **CW_HOST**: `https://api-na.myconnectwise.net`
- This is your API host (different from UI host)

3. **CW_CODEBASE**: `v2025_1`
- Already known from your URL
- Can be auto-detected if omitted

4. **CW_CLIENT_ID**: Your ConnectWise API Client ID
- Get this from ConnectWise Developer Portal
- Required even with public/private keys

5. **CW_PUBLIC_KEY** & **CW_PRIVATE_KEY**: Your API keys
- Generate these in ConnectWise (System → API Members → API Keys)
- Rotate and re-create them if needed

## Step 3: Test Your Connection

### Quick Test - Fetch Company Info
```bash
export CW_COMPANY="yourCompanyLoginName"
export CW_HOST="https://na.myconnectwise.net"
python fetch_company_info.py
```

### Full Smoke Test
```bash
export CW_COMPANY="yourCompanyLoginName"
export CW_HOST="https://api-na.myconnectwise.net"
export CW_CODEBASE="v2025_1"
export CW_CLIENT_ID="your-client-id"
export CW_PUBLIC_KEY="your-public-key"
export CW_PRIVATE_KEY="your-private-key"

# With Poetry:
poetry run python smoke_test_manage.py

# With venv:
python smoke_test_manage.py
```

## Common Issues

### 401/403 Errors
- **Invalid keys**: Regenerate your public/private keys in ConnectWise
- **Missing Client ID**: Ensure CW_CLIENT_ID is set correctly
- **Permissions**: Check your API member's security role has proper permissions

### 404 Errors
- **Wrong host**: Try `api-na.myconnectwise.net` instead of `na.myconnectwise.net`
- **Wrong codebase**: Verify codebase version matches your instance
- **Wrong path**: Ensure URL format is `https://api-na.myconnectwise.net/v2025_1/apis/3.0`

### Codebase Detection Fails
- Manually set `CW_CODEBASE` environment variable
- Or pass `codebase` parameter directly to `ConnectWiseManageAPIClient`

## Next Steps

Once your smoke test passes, you can:
1. Fetch your assigned tickets
2. Get recent activities
3. Create/update records (with proper permissions)

## Need Help?

If you're stuck, provide:
1. Your ConnectWise "Company" login name
2. Whether you have a Client ID (yes/no)

And I can help you troubleshoot further!
54 changes: 54 additions & 0 deletions fetch_company_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
Helper script to fetch company info from ConnectWise.

This can help you determine your company name and verify the codebase.

Usage:
export CW_COMPANY="yourCompanyLoginName" # Try different variations if unsure
export CW_HOST="https://na.myconnectwise.net" # Your UI host
python fetch_company_info.py
"""
import os
from urllib.parse import urlparse

import requests

CW_COMPANY = os.environ.get("CW_COMPANY", "")
CW_HOST = os.environ.get("CW_HOST", "https://na.myconnectwise.net")

# Parse hostname
parsed = urlparse(CW_HOST)
hostname = parsed.netloc or parsed.path.replace("https://", "").replace("http://", "").strip("/")

# Try to fetch company info
# The endpoint is: https://{hostname}/login/companyinfo/{company_name}
if not CW_COMPANY:
print("Please set CW_COMPANY environment variable")
print("Example: export CW_COMPANY='yourCompanyName'")
exit(1)

url = f"https://{hostname}/login/companyinfo/{CW_COMPANY}"
print(f"Fetching company info from: {url}")

try:
response = requests.get(url)
if response.status_code == 200:
data = response.json()
print("\n✓ Successfully retrieved company info:")
print(f" Company Name: {data.get('CompanyName', 'N/A')}")
print(f" Codebase: {data.get('Codebase', 'N/A')}")
print(f" Full URL: {data.get('FullUrl', 'N/A')}")
print("\nUse these values in your smoke test:")
print(f" export CW_COMPANY='{data.get('CompanyName', CW_COMPANY)}'")
print(f" export CW_CODEBASE='{data.get('Codebase', 'v2025_1')}'")
else:
print(f"\n✗ Failed with status {response.status_code}")
print(f" Response: {response.text}")
print("\nPossible issues:")
print(" - Company name is incorrect (try different variations)")
print(" - Company name might be case-sensitive")
print(" - Check if you need to use a different host (e.g., api-na.myconnectwise.net)")
except Exception as e:
print(f"\n✗ Error: {e}")
print("\nNote: This endpoint might require authentication or might not be accessible.")
print("You can also manually check your ConnectWise login page for the company name.")
64 changes: 64 additions & 0 deletions smoke_test_manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Smoke test for ConnectWise Manage API client.

This script performs a simple read-only test to verify API connectivity.

Usage:
export CW_COMPANY="yourCompanyLoginName"
export CW_HOST="https://api-na.myconnectwise.net"
export CW_CODEBASE="v2025_1" # optional, defaults to v2025_1
export CW_CLIENT_ID="your-client-id"
export CW_PUBLIC_KEY="your-public"
export CW_PRIVATE_KEY="your-private"
poetry run python smoke_test_manage.py
# OR
python smoke_test_manage.py
"""
import os

from pyconnectwise import ConnectWiseManageAPIClient

# Read environment variables
CW_COMPANY = os.environ["CW_COMPANY"] # login "Company"
CW_HOST = os.environ["CW_HOST"] # e.g. https://api-na.myconnectwise.net
CW_CODEBASE = os.environ.get("CW_CODEBASE", "v2025_1") # adjust if needed
CW_CLIENT_ID = os.environ["CW_CLIENT_ID"]
CW_PUBLIC = os.environ["CW_PUBLIC_KEY"]
CW_PRIVATE = os.environ["CW_PRIVATE_KEY"]

# Parse hostname from CW_HOST (remove https:// if present)
# The client expects just the hostname, not the full URL
manage_url = CW_HOST.replace("https://", "").replace("http://", "").strip("/")

print("Connecting to ConnectWise Manage API...")
print(f" Company: {CW_COMPANY}")
print(f" Host: {manage_url}")
print(f" Codebase: {CW_CODEBASE}")
print(f" Client ID: {CW_CLIENT_ID[:10]}..." if len(CW_CLIENT_ID) > 10 else f" Client ID: {CW_CLIENT_ID}")

# The client will construct the URL as: https://{manage_url}/{codebase}/apis/3.0
# So we pass just the hostname and codebase separately
api = ConnectWiseManageAPIClient(
CW_COMPANY,
manage_url,
CW_CLIENT_ID,
CW_PUBLIC,
CW_PRIVATE,
codebase=CW_CODEBASE,
)

# simplest read: list first 10 companies
print(f"\nFetching companies from {api._get_url()}...")
try:
companies = api.company.companies.get(params={"page": 1, "pageSize": 10})
print(f"✓ Successfully connected! Found {len(companies)} companies (showing first 10):\n")
for c in companies:
print(f" ID: {c.id}, Name: {getattr(c, 'name', 'N/A')}")
print("\n✓ Smoke test passed! API connection is working.")
except Exception as e:
print(f"\n✗ Smoke test failed with error: {e}")
print("\nCommon issues:")
print(" - 401/403: Check your keys, Client ID, or API member permissions")
print(" - 404: Verify your host and codebase are correct")
print(" - Network errors: Check your internet connection and firewall settings")
raise