HaloPSA Contracts — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited HaloPSA Contracts (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Contracts in HaloPSA define the service relationship with clients - what services you provide, how you bill for them, and what service levels apply. Contracts control how time, expenses, and recurring charges flow to invoices and are critical for MSP financial management.
A HaloPSA contract is the commercial agreement — coverage, rates, and the SLA you committed to. It is not the helpdesk configuration that computes a deadline, and it is not the bill.
business-hours calendars in the helpdesk are freshdesk-sla-business-hours; the contract states the commitment, the helpdesk approximates it, and the two can legitimately disagree.
are halopsa-invoices (read-only) and xero-invoices for the books.
differ materially; use autotask-contracts.
links are halopsa-assets.
| Type | Description | Billing Method |
|---|---|---|
| Recurring | Monthly/annual managed services | Fixed recurring fee |
| Prepaid Hours | Block hours/time bank | Deduct from balance |
| Ad-Hoc | Pay as you go (T&M) | Bill actual time |
| Project | Fixed-price project | Milestone billing |
| Warranty | Coverage period | No direct billing |
| Status | Description | Billing |
|---|---|---|
| Active | In effect | Billable |
| Pending | Not yet started | Not billable |
| Expired | Past end date | Not billable |
| Cancelled | Terminated early | Not billable |
| On Hold | Temporarily paused | Not billable |
A contract minimally needs ref (reference/name), client_id, startdate, status, and type. Billing behavior comes from billingfrequency and invoiceday; coverage from sla_id, includesallsites, and includesallassets; financials from value, setupfee, and renewalvalue.
See references/fields.md for the complete field reference (contract, billing, coverage, financial, recurring-item, and prepaid fields).
Contracts live at /api/ClientContract; recurring line items at /api/RecurringInvoiceItem. As elsewhere in HaloPSA, POST bodies are arrays, and an update is a POST that includes the record's id.
Two non-obvious expansion flags control what a GET returns:
| Query flag | Effect |
|---|---|
includerecurringinvoiceitems=true | Returns the contract's recurring line items |
includehoursummary=true | Returns prepaid hour totals, used, and remaining |
Date-range searches use enddate_before / enddate_after (combine with status=Active to find contracts that are expiring rather than already lapsed).
See references/api.md for full create/search/update request and response bodies, recurring-item payloads, prepaid contract creation, SLA linking, and renewal calls.
Recurring items are the line items that generate recurring invoices. Each item belongs to a contract (contract_id) and carries a description, quantity, and unitprice. An item can override the contract's billingfrequency and can have its own startdate/enddate, so mid-term additions (e.g. five new workstations) bill only from the date they start.
Prepaid contracts carry a purchased hour allocation (prepaid_hours) and an hourlyrate. Time entries logged against tickets linked to the contract automatically deduct from the balance - the ticket's time entry must carry contract_id for the deduction to happen. Query the balance with includehoursummary=true and alert before the remaining hours run out.
Setting sla_id on a contract makes tickets under that contract inherit:
status=Active with enddate_before set to your renewal horizonPending status with the new term dates and renewalvaluestatus to Expired once the renewal startsPoll prepaid_hours_remaining against a low-balance threshold (commonly 10 hours) and raise a replenishment quote before the block is exhausted, otherwise time silently accrues as unbilled.
For a billing period, compare expected recurring totals against invoiced amounts, then flag time entries linked to the contract that remain uninvoiced.
See references/examples.md for working implementations of validation, performance review, prepaid balance alerting, billing reconciliation, expiring-contract reports, and contract value by client.
| Code | Message | Resolution |
|---|---|---|
| 400 | ref required | Contract needs a reference name |
| 400 | client_id required | Must associate with a client |
| 400 | Invalid type | Use valid contract type |
| 400 | enddate before startdate | Fix date sequence |
| 404 | Contract not found | Verify contract ID |
| 409 | Cannot delete - has invoices | Cancel instead of delete |
Validate client-side before POSTing: prepaid-hours contracts are rejected without an hours allocation, and end dates preceding start dates fail on the server rather than being normalized.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.