Skip to main content

A Python toolkit for the ConnectWise Manage API with full type support

Project description

ConnectWise Manage Python Integration

A Python toolkit for the ConnectWise Manage API with full type support and error handling.

Python 3.9+ License: MIT

Features

  • 🎯 Typed Dataclasses - Ticket, Configuration, and Note models with convenient properties
  • 🔒 Credential Protection - SecretString wrapper prevents accidental password exposure
  • 🚨 Smart Error Handling - Specific exceptions for different error types (404s return None)
  • 🔄 Full HTTP Support - GET, POST, PATCH, PUT, DELETE methods
  • 📦 Opinionated Defaults - TicketDefaults for consistent ticket creation
  • 🎨 Clean API - No environment variable magic, explicit configuration

Installation

pip install connectwise-manage  # When published

For development:

pip install -e .

Quick Start

1. Initialize the Client

from connectwise import ConnectWiseClient

cw = ConnectWiseClient(
    base_url="https://connect.example.com",
    client="YourCompany",
    username="api_user",
    password="api_password",
    client_id="your-client-id-uuid"
)

2. Work with Tickets

# Get a ticket (returns Ticket object or None)
ticket = cw.get_ticket(ticket_id=12345)
if ticket:
    print(f"{ticket.summary} - {ticket.status_name}")
    print(f"Company: {ticket.company_name}")
    print(f"Priority: {ticket.priority_name}")

# Create a ticket
new_ticket = cw.create_ticket(
    summary="Server offline",
    body="PROD-WEB-01 is unreachable",
    company_id=250,
    board_id=1,
    priority_id=8
)
print(f"Created ticket #{new_ticket.id}")

# Update ticket status
updated = cw.update_ticket_status(
    ticket_id=new_ticket.id,
    status_id=456
)

# Add a note
note = cw.add_ticket_note(
    ticket_id=new_ticket.id,
    note_text="Investigating the issue",
    internal=True
)

3. Work with Configurations

# Find configurations by name
configs = cw.get_configurations(
    conditions='name contains "PROD" AND company/id=250'
)

for config in configs:
    print(f"{config.name} - IP: {config.ipAddress}")

# Attach configuration to ticket
cw.attach_configuration(
    ticket_id=12345,
    config_id=configs[0].id
)

4. Search and Filter

# Get open tickets for a company
tickets = cw.get_tickets(
    conditions="closedFlag=false AND company/id=250",
    pagesize=1000
)

# Search by summary text
urgent_tickets = cw.get_tickets(
    conditions='summary contains "urgent" AND priority/id>=8'
)

Using Ticket Defaults

For applications that create many similar tickets:

from connectwise import ConnectWiseClient, TicketDefaults

# Define defaults
defaults = TicketDefaults(
    company_id=250,
    board_id=1,
    priority_id=8,
    source_id=42  # RMM integration
)

# Initialize with defaults
cw = ConnectWiseClient(
    base_url="https://connect.example.com",
    client="YourCompany",
    username="api_user",
    password="api_password",
    client_id="uuid",
    ticket_defaults=defaults
)

# Create tickets without repeating parameters
ticket = cw.create_ticket(
    summary="Issue summary",
    body="Issue description"
    # company_id, board_id, priority_id, source_id use defaults
)

Error Handling

The integration uses specific exceptions for different error types:

from connectwise import (
    ConnectWiseAPIError,
    ConnectWiseNotFoundError,
    ConnectWiseRateLimitError,
    ConnectWiseBadRequestError
)

try:
    ticket = cw.update_ticket_status(ticket_id=123, status_id=456)
except ConnectWiseNotFoundError:
    print("Ticket doesn't exist")
except ConnectWiseRateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except ConnectWiseBadRequestError as e:
    print(f"Invalid request: {e}")
except ConnectWiseAPIError as e:
    print(f"API error [{e.status_code}]: {e}")

Note: 404s return None for get operations instead of raising exceptions:

ticket = cw.get_ticket(ticket_id=999999)
if ticket is None:
    print("Ticket not found")  # No exception raised

Dataclass Properties

All models provide convenient properties for nested data:

ticket = cw.get_ticket(ticket_id=12345)

# Clean property access
print(ticket.company_name)      # Instead of ticket.company.get('name')
print(ticket.status_name)       # Instead of ticket.status.get('name')
print(ticket.priority_name)     # Instead of ticket.priority.get('name')

# Datetime parsing included
if ticket.is_closed:
    print(f"Closed: {ticket.closed_datetime}")  # Returns datetime object

# Configuration properties
config = cw.get_configuration(config_id=67890)
print(f"{config.name} - {config.type_name}")
print(f"Company: {config.company_name}")
print(f"Active: {config.is_active}")

Advanced Usage

Custom Fields and Filtering

# Get tickets with specific fields only (returns raw dict)
result = cw.get(
    "service/tickets",
    conditions="closedFlag=false",
    fields="id,summary,status"
)

# Use base HTTP methods for custom endpoints
result = cw.post("custom/endpoint", data={...})
result = cw.patch("service/tickets", record_id=123, operations=[...])
success = cw.delete("service/tickets", record_id=123)

Complete Workflow Example

# Find server configuration
configs = cw.get_configurations(
    conditions='name="PROD-WEB-01" AND company/id=250'
)

if configs:
    config = configs[0]

    # Create ticket
    ticket = cw.create_ticket(
        summary=f"Server {config.name} Offline",
        body=f"Server unreachable. IP: {config.ipAddress}",
        company_id=250,
        board_id=1,
        priority_id=8
    )

    # Attach configuration
    cw.attach_configuration(ticket.id, config.id)

    # Add investigation note
    cw.add_ticket_note(
        ticket_id=ticket.id,
        note_text="Checking server connectivity",
        internal=True
    )

    # Get ticket URL for external systems
    ticket_url = cw.get_ticket_url(ticket.id)
    print(f"View ticket: {ticket_url}")

Performance Tips

  1. Use specific conditions to limit results:

    # ❌ Slow - fetches everything
    tickets = cw.get_tickets()
    
    # ✅ Fast - filtered query
    tickets = cw.get_tickets(conditions="company/id=250 AND closedFlag=false")
    
  2. Leverage the fields parameter for partial data:

    result = cw.get("service/tickets", fields="id,summary,status")
    
  3. Handle rate limits gracefully:

    import time
    
    try:
        tickets = cw.get_tickets(conditions="...")
    except ConnectWiseRateLimitError as e:
        if e.retry_after:
            time.sleep(e.retry_after)
            tickets = cw.get_tickets(conditions="...")
    

Credential Security

Passwords are automatically wrapped in SecretString to prevent accidental exposure in logs:

cw = ConnectWiseClient(...)
print(cw._password)  # Output: **********

ConnectWise Conditions Syntax

ConnectWise uses a specific syntax for filtering:

# Basic operators: =, !=, <, >, <=, >=
conditions = "closedFlag=false"

# Logical operators: AND, OR
conditions = "closedFlag=false AND company/id=250"

# String operators: contains, like
conditions = 'summary contains "server"'
conditions = 'name like "PROD-%"'

# Nested properties
conditions = "company/id=250 AND status/name='Open'"

# Complex conditions
conditions = "(closedFlag=false AND priority/id>=8) OR summary contains 'urgent'"

Documentation

Requirements

  • Python 3.9+
  • requests library

Development

# Clone the repository
git clone https://github.com/Ruderali/ConnectPyse.git
cd ConnectPyse

# Install in development mode
pip install -e .

# Run tests
pytest

Contributing

Contributions are welcome! This library aims to stay a pure API wrapper without business logic.

Design Principles:

  • Keep it simple - no complex abstractions
  • Return dataclasses from high-level methods
  • Return raw JSON from base HTTP methods
  • 404s return None for get operations
  • Raise specific exceptions for errors
  • No environment variable reading
  • No business logic

License

MIT License - see LICENSE file for details

Related Projects

  • npycentral - N-Central RMM Python integration (same author)

Support

For issues and questions:


Note: This is an unofficial library and is not affiliated with or endorsed by ConnectWise.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

connectpyse_manage-0.0.1.tar.gz (14.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

connectpyse_manage-0.0.1-py3-none-any.whl (18.0 kB view details)

Uploaded Python 3

File details

Details for the file connectpyse_manage-0.0.1.tar.gz.

File metadata

  • Download URL: connectpyse_manage-0.0.1.tar.gz
  • Upload date:
  • Size: 14.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for connectpyse_manage-0.0.1.tar.gz
Algorithm Hash digest
SHA256 1af9360e1f57bd61127b85a69497e2b9fd36455e13acc422c27bf11397565eb9
MD5 01b02868da8794d90e1fd434c0edf651
BLAKE2b-256 2b46f9b16c5a1f51850fb486356b83d54ea8c022a3b966fef8e6e3af56abc586

See more details on using hashes here.

File details

Details for the file connectpyse_manage-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for connectpyse_manage-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 88f2f9119783857a2e69c7a422db4f2a3a7d821025d9a4813faab4c77bdea717
MD5 5e54b830640b6a875d09a9c1d6bc8914
BLAKE2b-256 488c71721befc52d76cb07cd1958bd76d7653e4086382fadc4096a80b40237b6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page