ConnectWise Manage
ConnectWise agreements, and where the billing leaks
In ConnectWise Manage the contract is the agreement, but the billable lines live one level down in the agreement's additions, and that split is where most billing gaps start. The six common leaks are additions whose quantity never matched reality, time entries with billableOption set to something other than Billable, agreements past their end date still taking work, prepaid agreements drawn beyond their allowance, work with no agreement at all, and agreements that never invoiced in a cycle. All six are visible through the API, and none of them appear on a standard report.
Kyslan finds the unbilled work in your own PSA, then writes the change order that bills it. First report free, read-only. Claim it back →
How a ConnectWise agreement is actually shaped
An agreement in ConnectWise Manage is a header. It carries the client, the type, the dates and the billing cycle. It does not carry the money in any useful detail.
The money lives in additions, a child list on each agreement. One addition per billable thing: a block of seats, a device class, a hosted line, a licence. Each addition has its own quantity, unit price and effective dates. An agreement with the right header and a stale addition quantity looks perfectly healthy in every summary view and under-bills every month.
Time is joined separately. A time entry carries a chargeToType and a chargeToId, which say what the time was charged against: a ticket, an activity, a project or the agreement itself. Reconciling delivery against entitlement means pulling agreements, walking every agreement's additions, pulling time entries for the period, and joining the two through the charge-to reference. There is no single view that does this.
Any check that reads only the agreement header will find nothing wrong, because the header rarely goes stale. The quantities on the additions are what drift, and they drift every time a client hires someone or you deploy an agent.
The six leaks
1. Addition quantities that stopped matching reality
The largest leak in most ConnectWise shops. A client grows, the service desk onboards the users, and nobody edits the addition quantity on the agreement. Compare each addition's quantity against the live count for whatever it represents: users in the tenant, agents in the RMM, licences in the vendor portal. Anything more than one out is a finding, and it recurs every cycle until someone edits it.
2. Time entries flagged as not billable
A ConnectWise time entry carries billableOption, and only the value Billable results in a charge. The other values, including no-charge and do-not-bill, are all legitimate settings that are frequently applied by accident, by a default on a board or a ticket type rather than by a person deciding.
Pull 90 days of time entries, group non-billable hours by ticket type, by board and by member. A cluster on one board or one type is a configuration problem you fix once. A cluster on one member is a training conversation. Both are far more common than a genuine spread of individually justified no-charges.
3. Work delivered past the agreement end date
ConnectWise will happily accept time against an agreement whose term has ended. Nothing stops it and nothing shouts. List agreements whose end date has passed, then check whether time entries exist after that date. The two outcomes are both bad: still delivering and not billing, or still billing with no live agreement.
4. Prepaid agreements drawn past the allowance
Retainer and prepaid-hours agreements have an allowance per period. Hours beyond it should trigger an overage charge or a top-up. In practice the engineer logs the time, the agreement absorbs it, and the overage never reaches an invoice.
Compare hours logged per period against the allowance for the same period, and remember that carried-over hours change the answer. Any month over the allowance where no overage was invoiced is a finding, priced at your out-of-scope rate.
5. Work delivered with no agreement at all
Time logged against a client who has no active agreement covering it. Sometimes it is genuinely time and materials and the invoice went out fine. Often it is a client whose agreement lapsed, or a new client onboarded before the paperwork completed. Check whether an invoice exists for that time. If not, that is delivered work with neither entitlement nor charge.
6. Agreements that never invoiced in a cycle
An agreement worth real money per cycle where the invoice for that cycle was never raised. Causes are mundane: a billing flag off, a client on hold from six months ago, an agreement excluded from a batch. The amount is a full cycle of revenue and it is invisible because nothing on the agreement changed.
Fields that matter, and two that mislead
Two field-level notes worth having, both learned by running against a live instance rather than by reading documentation:
| What you want | Where it is | Watch out |
|---|---|---|
| The agreement's own scope wording | internalNotes | There is no comment field on an agreement. Scope prose written elsewhere is not readable |
| How often it bills | billingCycle | Not invoicingCycle. That field says which period the agreement invoices against, such as calendar or contract year, not how frequently it bills. Treating it as a cadence divides real money by twelve |
| When a ticket was raised | Inside _info | Not on the ticket root, so a naive read returns undated tickets and every date-based check silently disables itself |
| Whether time bills | billableOption === "Billable" | Any other value produces no charge |
| What time was charged to | chargeToType plus chargeToId | Only ticket charge types join to a ticket |
ConnectWise evaluates the date bounds in a query condition using the instance's own timezone, while returning values in UTC. A filter with an upper bound of "now" can therefore hide the most recent few hours, and a query for a period that ends today can come back empty. Widen the window by a whole day at each end when you are checking recent work.
Why the built-in reports miss this
ConnectWise reporting is genuinely capable, and the leaks above still get through it, for three structural reasons:
- Reports answer within one object. Agreements, time entries and invoices each report well. Every leak here is a mismatch between two of them.
- The comparison needs an outside source. Whether an addition quantity is correct cannot be answered from ConnectWise at all: the truth lives in the client's directory, your RMM, or the vendor's licence portal.
- Absence does not appear in reports. A ticket closed with no time entry, an invoice never raised, an overage never charged. Reports list records that exist. Every one of these leaks is a record that does not.
Getting read-only access to check
ConnectWise does not offer a sign-in-with-ConnectWise flow for outside tools, so read-only API keys are the only way any product reads your data. The right pattern is a dedicated API member with a restricted security role, not a key attached to a person, so the access is auditable and revocable without touching anyone's login.
We wrote a step by step for it: the sixty second ConnectWise key setup. It is the same process whatever tool you are connecting.
Kyslan runs all six checks read-only through that key, prices each finding, and re-scans weekly. The first audit is free, or see a sample report first.
Common questions
Where does ConnectWise Manage keep the billable value of an agreement?
On the agreement's additions, not on the agreement header. The header carries the client, type, dates and billing cycle, while each addition carries a quantity, unit price and its own effective dates. That is why an agreement can look correct in every summary view while under-billing every cycle: the header never went stale, the addition quantity did.
How do I find unbilled time in ConnectWise?
Pull 90 days of time entries and group hours where billableOption is not Billable, cut by ticket type, board and member. Clusters on a single board or ticket type indicate a default set the wrong way rather than individual judgement. Then separately list closed tickets carrying no time entry at all, which is delivered work with no record to bill from.
What is the difference between billingCycle and invoicingCycle in ConnectWise?
billingCycle is how often the agreement bills, such as monthly or quarterly. invoicingCycle describes which period the agreement invoices against, with values such as calendar year or contract year. They are not interchangeable, and reading invoicingCycle as a cadence produces figures that are wrong by a factor of twelve.
Can ConnectWise stop time being logged against an expired agreement?
By default it does not prevent it, and no alert is raised. Time can be recorded against an agreement whose end date has passed, which produces either delivered work that is never billed or billing with no live agreement behind it. Finding it means listing agreements with a past end date and checking for time entries dated after it.
Do I need write access to audit ConnectWise billing?
No. Every check described here is a read. The safest arrangement is a dedicated API member with a restricted, read-only security role, which keeps the access auditable and lets you revoke it without touching any person's login. ConnectWise has no sign-in flow for third party tools, so API keys are the only mechanism available.
Keep reading
See your own number, then send the invoice for it
Kyslan reads 90 days of your tickets, time entries and contracts, finds the work you delivered and never billed, then writes the change order that puts it back on the invoice. Read only, nothing installed, first report free.
Claim it backFree · read-only key · no card · HaloPSA, ConnectWise Manage, Autotask PSA or a CSV export