Skip to content
API & Integrations11 min read

Building Custom PSA Integrations With REST APIs

Cory Neese·

At some point, every MSP hits a wall where native integrations don't exist or don't do enough. A vendor tool doesn't have a ConnectWise integration. A business process requires data to flow between systems in a way no off-the-shelf connector supports. A reporting need requires pulling data from three different APIs into one dashboard.

That's when you build a custom integration. I've built a lot of them — some simple, some complex — and the patterns that make them reliable are surprisingly consistent regardless of what you're connecting.

This isn't a programming tutorial. It's a practical guide to the decisions, patterns, and pitfalls that determine whether your custom integration works reliably in production or becomes another thing someone has to babysit.

Authentication: get this right first

Both ConnectWise and HaloPSA use API keys for authentication, but the implementation differs.

ConnectWise uses a combination of company ID, public key, and private key, sent as a Base64-encoded Authorization header. The keys are tied to a member account in ConnectWise, which means that member's permissions determine what the API can access. Create a dedicated API member account with the specific permissions your integration needs — don't use a real person's credentials.

HaloPSA uses OAuth2 client credentials flow. You register an API application in the admin panel, get a client ID and secret, and exchange those for an access token. The token expires (typically after an hour), so your integration needs to handle token refresh automatically.

The mistake I see most often: storing API credentials in source code or configuration files without encryption. API keys are credentials. Treat them like passwords. Use environment variables or a secrets manager, and rotate them periodically.

Designing the data flow

Before writing any code, document the data flow on paper or a whiteboard. For each integration, answer:

What data moves? Be specific. Not "ticket data" — which fields? Ticket ID, summary, status, assigned tech, time entries, notes?

In which direction? One-way (PSA → external system), one-way (external → PSA), or bidirectional?

How often? Real-time via webhooks, scheduled sync every 15 minutes, daily batch, or on-demand?

What's the source of truth? When the same data exists in both systems, which one wins in a conflict? This decision prevents the nightmare scenario where two systems overwrite each other in an infinite loop.

What happens when it fails? Because it will fail. Network timeouts, API rate limits, authentication expiry, malformed data. Every failure mode needs a handling strategy.

I document this as a simple table before writing any code. It takes 30 minutes and prevents days of rework.

Common integration patterns

Most PSA integrations fall into a few recurring patterns:

Alert-to-ticket creation. An external system detects an event (monitoring alert, security incident, form submission) and creates a ticket in the PSA. This is one-way, usually real-time or near-real-time, and the most common custom integration I build.

The key detail: idempotency. If the external system sends the same alert twice (network retry, duplicate webhook), your integration should detect the duplicate and not create a second ticket. Use a unique identifier from the source system — alert ID, incident number — and check for its existence before creating a new ticket.

Bidirectional status sync. Two systems need to reflect the same status. A ticket's status in the PSA should match the corresponding record in a project management tool, CRM, or vendor portal. This requires careful mapping (status names won't match between systems) and conflict resolution (what happens if both sides update simultaneously).

Billing data aggregation. Pull usage data from a vendor API (license counts, agent counts, consumption metrics) and push it into PSA agreement additions or billing line items. This is usually a scheduled batch process — run it nightly or weekly, compare current counts against what's in the PSA, and update the delta.

Reporting data pipeline. Extract data from the PSA and push it to a data warehouse, BI tool, or custom dashboard. This is one-way, batch-oriented, and the primary challenge is handling the volume efficiently. ConnectWise and HaloPSA both have pagination in their APIs — use it correctly instead of trying to pull everything in a single request.

Error handling that works in production

The difference between a prototype integration and a production integration is error handling. Specifically:

Retry with backoff. When an API call fails due to a timeout or rate limit, don't immediately retry at full speed. Wait a few seconds, try again. If it fails again, wait longer. Exponential backoff with a cap (e.g., 1s, 2s, 4s, 8s, max 60s) is the standard approach. Both ConnectWise and HaloPSA will rate-limit aggressive callers.

Meaningful logging. Log every API call and response (at debug level), every error (at error level), and every business-logic decision (at info level). When something goes wrong at 2am, the logs are your only diagnostic tool. "API call failed" is useless. "POST /service/tickets returned 429 Rate Limit Exceeded at 02:14:33 - retrying in 8s" is actionable.

Alerting on persistent failures. If an integration fails three times in a row, send a notification. Email, Slack, SMS — something that a human will see. Silent failures are the worst kind of failures because they can persist for days or weeks before anyone notices.

Dead letter queue. When a record can't be processed after all retries, don't drop it. Put it in a queue for manual review. This ensures nothing is permanently lost even when the automation can't handle it.

ConnectWise API specifics

A few things I've learned from extensive work with ConnectWise's API:

The REST API is the one to use. The older SOAP API still exists but should be avoided for new development. The REST endpoints cover most of what you need and are actively maintained.

Pagination is mandatory for list endpoints. The default page size is 25, maximum is 1000. Always implement pagination in your code, even if you think the result set is small. Datasets grow.

Some endpoints have undocumented quirks. Certain fields are read-only via API even though they're editable in the UI. Some PUT operations require including fields that weren't in the GET response. When something doesn't work as the documentation suggests, check the community forums — someone else has probably hit the same issue.

Date/time handling uses ISO 8601 format, but timezone behavior can be inconsistent across endpoints. Always test with timezone-sensitive data and verify the results match expectations.

HaloPSA API specifics

HaloPSA's API is genuinely pleasant to work with. A few notes:

The OAuth2 token flow is standard and well-documented. Just remember to handle token expiration in your code — tokens last about an hour, and your integration needs to refresh automatically.

The API documentation is comprehensive and generally accurate. When I find discrepancies, HaloPSA's support team is responsive about correcting them.

HaloPSA supports webhooks natively, which means you can build event-driven integrations without polling. When a ticket status changes, HaloPSA can call your endpoint immediately. This is faster and more efficient than periodic polling.

The API supports filtering and sorting on most list endpoints, which means you can pull exactly the data you need instead of fetching everything and filtering client-side. Use this — it reduces API calls and response payload sizes significantly.

When to build vs. when to use middleware

Not every integration needs to be built from scratch. Platforms like Rewst and Power Automate can handle many common MSP integration scenarios with drag-and-drop workflow builders. If the integration is a standard pattern (alert to ticket, data sync between two systems with existing connectors), middleware might be faster and more maintainable than custom code.

Build custom when: the logic is complex enough that a visual workflow builder becomes unwieldy, you need fine-grained control over error handling and retry logic, performance matters (custom code is almost always faster than middleware for high-volume integrations), or the systems involved don't have connectors in your middleware platform.

Use middleware when: the integration is a standard pattern with existing connectors on both sides, the development team is small or nonexistent (middleware is more accessible to non-developers), and the volume is moderate (middleware handles hundreds of operations per day easily, but may struggle with tens of thousands).


Need a custom integration built? Book a call and let's scope it out.

Cory Neese

Founder & PSA Consultant at PaxRig

Cory helps MSPs get more out of their ConnectWise and HaloPSA platforms through expert configuration, migration, and automation. He founded PaxRig to bring enterprise-level PSA expertise to the MSP channel.

Need help implementing this?

Book a free discovery call and I'll walk through your specific situation.