Receber por email
← Voltar para o catálogo

ConnectWise Manage API Patterns

Use this skill when working with the ConnectWise PSA REST API - authentication using public/private keys and clientId, pagination with page/pageSize, conditions query syntax, rate limiting (60/min), and error handling. Covers all common API patterns for ConnectWise PSA integration.

31estrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: Apache-2.0

Como adicionar

/plugin marketplace add wyre-technology/msp-claude-plugins

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/connectwise-manage-api-patterns/svg)](https://www.skillteca.com.br/skills/connectwise-manage-api-patterns)

Skills relacionadas

Ver todas de Automação →

Alerta por categoria

Receba novas skills de Automação toda segunda

1 email curto, só com as skills novas de Automação. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

ConnectWise PSA API Patterns

Overview

The ConnectWise PSA REST API provides access to all PSA entities including tickets, companies, contacts, projects, and time entries. This skill covers authentication, query syntax, pagination, rate limiting, and best practices for API integration.

Base URLs

| Region | Base URL | |--------|----------| | North America | https://api-na.myconnectwise.net/{codebase}/apis/3.0/ | | Europe | https://api-eu.myconnectwise.net/{codebase}/apis/3.0/ | | Australia | https://api-au.myconnectwise.net/{codebase}/apis/3.0/ |

Replace {codebase} with your company identifier (e.g., v4_6_release or custom).

Legacy URLs

Some instances may use legacy URLs:

https://api-na.myconnectwise.net/v4_6_release/apis/3.0/
https://api-staging.connectwisedev.com/v4_6_release/apis/3.0/

Authentication

Public/Private Key + Client ID

ConnectWise PSA uses Basic Authentication with a combined credential string plus a Client ID header.

Credential Format

Authorization: Basic base64({companyId}+{publicKey}:{privateKey})
clientId: {your-client-id}

Step-by-Step Authentication

  1. Combine credentials:

`` companyId + "+" + publicKey + ":" + privateKey Example: company+publickey:privatekey ``

  1. Base64 encode:

`` base64("company+publickey:privatekey") = "Y29tcGFueStwdWJsaWNrZXk6cHJpdmF0ZWtleQ==" ``

  1. Set headers:

``http Authorization: Basic Y29tcGFueStwdWJsaWNrZXk6cHJpdmF0ZWtleQ== clientId: your-registered-client-id Content-Type: application/json ``

Example Request

GET /v4_6_release/apis/3.0/service/tickets
Host: api-na.myconnectwise.net
Authorization: Basic Y29tcGFueStwdWJsaWNrZXk6cHJpdmF0ZWtleQ==
clientId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Content-Type: application/json

JavaScript Authentication Example

const companyId = process.env.CONNECTWISE_COMPANY_ID;
const publicKey = process.env.CONNECTWISE_PUBLIC_KEY;
const privateKey = process.env.CONNECTWISE_PRIVATE_KEY;
const clientId = process.env.CONNECTWISE_CLIENT_ID;

const credentials = `${companyId}+${publicKey}:${privateKey}`;
const base64Credentials = Buffer.from(credentials).toString('base64');

const headers = {
  'Authorization': `Basic ${base64Credentials}`,
  'clientId': clientId,
  'Content-Type': 'application/json'
};

Obtaining Credentials

  1. API Member: Create in System > Members > API Members
  2. Public/Private Keys: Generate for API member
  3. Client ID: Register at ConnectWise Developer Portal

Conditions Query Syntax

Basic Syntax

conditions=field operator value

Supported Operators

| Operator | Description | Example | |----------|-------------|---------| | = | Equals | status/id=1 | | != | Not equals | status/id!=5 | | < | Less than | priority/id<3 | | <= | Less than or equal | priority/id<=2 | | > | Greater than | dateEntered>2024-01-01 | | >= | Greater than or equal | dateEntered>=2024-01-01 | | contains | Contains substring | summary contains "email" | | like | Pattern match | summary like "%email%" | | in | In list | status/id in (1,2,3) | | not in | Not in list | status/id not in (5) |

Field References

Use / to reference nested fields:

company/id=12345
status/name="New"
contact/firstName contains "John"

Combining Conditions

AND (default):

conditions=company/id=12345 and status/id!=5 and priority/id<=2

OR:

conditions=status/id=1 or status/id=2

Complex:

conditions=(status/id=1 or status/id=2) and company/id=12345

Date Conditions

Date format: YYYY-MM-DD or ISO 8601

conditions=dateEntered>=[2024-01-01]
conditions=dateEntered>=[2024-01-01T00:00:00Z] and dateEntered<[2024-02-01T00:00:00Z]

String Conditions

Exact match:

conditions=summary="Email not working"

Contains:

conditions=summary contains "email"

Like (wildcards):

conditions=summary like "%email%"
conditions=company/identifier like "AC%"

Null Checks

conditions=contact=null
conditions=assignedResource!=null

URL Encoding

Special characters must be URL-encoded:

| Character | Encoded | |-----------|---------| | Space | %20 | | = | %3D | | < | %3C | | > | %3E | | " | %22 |

Example:

GET /service/tickets?conditions=company/id%3D12345%20and%20status/id!%3D5

Pagination

Request Parameters

| Parameter | Type | Default | Max | Description | |-----------|------|---------|-----|-------------| | page | int | 1 | - | Page number (1-based) | | pageSize | int | 25 | 1000 | Records per page |

Example Request

GET /service/tickets?page=1&pageSize=100

Response Headers

| Header | Description | |--------|-------------| | Link | Contains next/prev page URLs | | X-Total-Count | Total record count (if requested) |

Pagination Example

async function fetchAllTickets(conditions) {
  const allTickets = [];
  let page = 1;
  const pageSize = 250;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(
      `${baseUrl}/service/tickets?conditions=${conditions}&page=${page}&pageSize=${pageSize}`,
      { headers }
    );

    const tickets = await response.json();
    allTickets.push(...tickets);

    hasMore = tickets.length === pageSize;
    page++;
  }

  return allTickets;
}

Getting Total Count

GET /service/tickets?conditions=status/id!=5&pageSize=1&fields=id

Check X-Total-Count header or use /count endpoint:

GET /service/tickets/count?conditions=status/id!=5

Rate Limiting

Limits

| Limit | Value | |-------|-------| | Requests per minute | 60 | | Per API member | Yes |

Rate Limit Headers

| Header | Description | |--------|-------------| | X-RateLimit-Limit | Maximum requests per minute | | X-RateLimit-Remaining | Requests remaining in window | | X-RateLimit-Reset | Seconds until limit resets |

429 Response

When rate limited, you receive HTTP 429:

{
  "code": "RateLimitExceeded",
  "message": "Rate limit exceeded. Try again in 30 seconds."
}

Retry Strategy

async function requestWithRetry(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || 30;
      const jitter = Math.random() * 1000;
      await sleep(retryAfter * 1000 + jitter);
      continue;
    }

    return response;
  }
  throw new Error('Max retries exceeded');
}

Best Practices for Rate Limits

  1. Implement exponential backoff - Don't hammer the API
  2. Check headers - Monitor remaining requests
  3. Batch operations - Reduce total requests
  4. Cache reference data - Queues, statuses, members
  5. Use webhooks - Instead of polling for changes

Error Handling

HTTP Status Codes

| Code | Meaning | Action | |------|---------|--------| | 200 | Success | Process response | | 201 | Created | Entity created | | 204 | No Content | Delete successful | | 400 | Bad Request | Check request format | | 401 | Unauthorized | Verify credentials | | 403 | Forbidden | Check permissions | | 404 | Not Found | Entity doesn't exist | | 409 | Conflict | Record locked/modified | | 429 | Rate Limited | Implement backoff | | 500 | Server Error | Retry with backoff |

Error Response Format

{
  "code": "InvalidArgument",
  "message": "The value 'invalid' is not valid for field 'status/id'.",
  "errors": [
    {
      "code": "InvalidArgument",
      "message": "status/id must be a valid integer",
      "field": "status/id"
    }
  ]
}

Common Errors

| Error | Cause | Resolution | |-------|-------|------------| | InvalidCredentials | Bad auth | Verify company ID, keys | |

Compartilhar X LinkedIn