HaloPSA Contract Management
Overview
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.
Key Concepts
Contract Types
| 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 |
Contract Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | int | System | Unique identifier |
ref | string | Yes | Contract reference/name |
client_id | int | Yes | Associated client |
startdate | date | Yes | Contract start |
enddate | date | No | Contract end |
status | string | Yes | Contract status |
type | string | Yes | Contract type |
Billing Fields
| Field | Type | Required | Description |
|---|---|---|---|
billingfrequency | string | No | Monthly, Quarterly, Annual |
invoiceday | int | No | Day of month to invoice |
taxcode | string | No | Tax code for invoicing |
currency_code | string | No | Billing currency |
poref | string | No | Purchase order reference |
Coverage Fields
| Field | Type | Required | Description |
|---|---|---|---|
sla_id | int | No | Associated SLA |
priority_id | int | No | Default ticket priority |
includesallsites | bool | No | Covers all client sites |
includesallassets | bool | No | Covers all assets |
Financial Fields
| Field | Type | Required | Description |
|---|---|---|---|
value | decimal | No | Contract value |
setupfee | decimal | No | One-time setup fee |
renewalvalue | decimal | No | Renewal amount |
marginpercent | decimal | No | Target margin |
Contract Status
| 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 |
API Patterns
Creating a Contract
POST /api/ClientContract
Authorization: Bearer {token}
Content-Type: application/json
[
{
"ref": "Acme Corp - Managed Services 2024",
"client_id": 123,
"type": "Recurring",
"status": "Active",
"startdate": "2024-01-01",
"enddate": "2024-12-31",
"billingfrequency": "Monthly",
"invoiceday": 1,
"value": 2500.00,
"sla_id": 1,
"includesallsites": true,
"includesallassets": true,
"notes": "Premium support tier, includes unlimited remote support"
}
]
Response
{
"contracts": [
{
"id": 5001,
"ref": "Acme Corp - Managed Services 2024",
"client_id": 123,
"client_name": "Acme Corporation",
"status": "Active",
"startdate": "2024-01-01",
"enddate": "2024-12-31"
}
]
}
Searching Contracts
By client:
GET /api/ClientContract?client_id=123
Active contracts:
GET /api/ClientContract?status=Active
Expiring soon:
GET /api/ClientContract?enddate_before=2024-03-31&enddate_after=2024-01-01&status=Active
By type:
GET /api/ClientContract?type=Recurring
Getting a Single Contract
GET /api/ClientContract/5001
With recurring items:
GET /api/ClientContract/5001?includerecurringinvoiceitems=true
Updating a Contract
POST /api/ClientContract
Authorization: Bearer {token}
Content-Type: application/json
[
{
"id": 5001,
"status": "On Hold",
"notes": "Client requested temporary pause - resume Feb 2024"
}
]
Recurring Items
Recurring items are the line items that generate recurring invoices.
Recurring Item Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | int | System | Unique identifier |
contract_id | int | Yes | Parent contract |
description | string | Yes | Line item description |
quantity | decimal | Yes | Quantity |
unitprice | decimal | Yes | Price per unit |
billingfrequency | string | No | Override contract frequency |
startdate | date | No | Item start date |
enddate | date | No | Item end date |
Creating Recurring Items
POST /api/RecurringInvoiceItem
Authorization: Bearer {token}
Content-Type: application/json
[
{
"contract_id": 5001,
"description": "Managed Workstation Support",
"quantity": 25,
"unitprice": 50.00,
"startdate": "2024-01-01"
},
{
"contract_id": 5001,
"description": "Server Management",
"quantity": 3,
"unitprice": 200.00,
"startdate": "2024-01-01"
},
{
"contract_id": 5001,
"description": "M365 Business Premium Licenses",
"quantity": 25,
"unitprice": 25.00,
"startdate": "2024-01-01"
}
]
Updating Recurring Items
[
{
"id": 10001,
"quantity": 30,
"notes": "Added 5 workstations in March"
}
]
Prepaid Hours (Block Hours)
Prepaid Contract Fields
| Field | Type | Description |
|---|---|---|
prepaid_hours | decimal | Total hours purchased |
prepaid_hours_used | decimal | Hours consumed |
prepaid_hours_remaining | decimal | Available balance |
hourlyrate | decimal | Rate per hour |
Creating Prepaid Contract
[
{
"ref": "Acme Corp - Prepaid Hours Q1 2024",
"client_id": 123,
"type": "Prepaid Hours",
"status": "Active",
"startdate": "2024-01-01",
"enddate": "2024-03-31",
"prepaid_hours": 40,
"hourlyrate": 150.00,
"value": 6000.00
}
]
Checking Hours Balance
GET /api/ClientContract/5002?includehoursummary=true
Response includes:
{
"prepaid_hours": 40,
"prepaid_hours_used": 12.5,
"prepaid_hours_remaining": 27.5
}
Hours Deduction
Time entries against tickets linked to prepaid contracts automatically deduct hours:
{
"ticket_id": 54321,
"timetaken": 60,
"contract_id": 5002
}
Contract-SLA Association
Link contracts to Service Level Agreements:
[
{
"id": 5001,
"sla_id": 1
}
]
SLA Enforcement
Tickets under contract inherit SLA settings:
- Response time targets
- Resolution time targets
- Business hours definitions
- Escalation rules
Common Workflows
Contract Setup
-
Create contract
- Set type, dates, status
- Associate SLA
- Configure billing
-
Add recurring items
- Define services included
- Set pricing and quantities
-
Link to assets (optional)
- Covered devices
- License tracking
-
Configure billing
- Invoice frequency
- Payment terms
- Tax settings
Contract Renewal
-
Identify expiring contracts
GET /api/ClientContract?enddate_before=2024-03-31&status=Active -
Review performance
async function reviewContractPerformance(contractId) { const contract = await getContract(contractId); const tickets = await getContractTickets(contractId); return { totalTickets: tickets.length, slaCompliance: calculateSLACompliance(tickets), hoursUsed: calculateHoursUsed(tickets), profitability: calculateProfitability(contract, tickets) }; } -
Generate renewal
[ { "ref": "Acme Corp - Managed Services 2025", "client_id": 123, "type": "Recurring", "status": "Pending", "startda