HaloPSA CRM data model: client records and their billing/contact fields, sites (locations), contacts (Users), client classification, and parent-child client hierarchy. Covers the Client/Site/Users API surface, onboarding and deactivation workflows, duplicate prevention, and common validation errors.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add WYRE-AI/msp-claude-plugins --skill clients --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Clients?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/wyre-ai-clients-6c178157)More formats (shields.io, HTML) on the badges page.
---
name: "HaloPSA Clients"
description: >
HaloPSA CRM data model: client records and their billing/contact fields,
sites (locations), contacts (Users), client classification, and parent-child
client hierarchy. Covers the Client/Site/Users API surface, onboarding and
deactivation workflows, duplicate prevention, and common validation errors.
when_to_use: >-
When creating, updating, searching, or managing customer relationships in HaloPSA.
Use when: halopsa client, halo client, halopsa customer, halo customer, client management
halopsa, create client halopsa, halopsa site, halopsa location, halopsa contact, client
onboarding halo, or halopsa crm.
---
# HaloPSA Client Management
## Overview
Clients (customers) are the foundation of HaloPSA. All tickets, contracts, assets, and invoices are associated with clients. Proper client data management is critical for accurate service delivery and billing.
## Anti-triggers
HaloPSA calls end-user contacts **Users**. That word does not mean a
HaloPSA login, and it does not mean a technician.
- **Your own technicians and teams** — those are agents, a separate
entity with its own IDs; use `halopsa-agents`. Assigning a ticket needs
an `agent_id`, never a `user_id`.
- **The same customer in another system** — a Freshdesk company, a
ConnectWise company, or an Autotask company is a different record with
different required fields; use `freshdesk-contacts-companies`,
`connectwise-psa-companies`, or `autotask-crm`. Do not assume IDs or
domains map across.
- **Microsoft 365 or Entra user accounts** — mailbox, licence, and
identity questions are `cipp-users` or `cipp-mailboxes`; a HaloPSA User
is a CRM contact record with no directory presence.
- **What the client is entitled to** — coverage, rates, and prepaid hours
are `halopsa-contracts`; what they have been billed is
`halopsa-invoices`.
## Key Concepts
Three linked entities make up the CRM layer:
| Entity | Endpoint | Key relationship |
|--------|----------|------------------|
| Client | `/api/Client` | Root record; owns sites, contacts, tickets, assets, contracts |
| Site | `/api/Site` | `client_id` -> Client; one site may be flagged `main_site` |
| Contact (User) | `/api/Users` | `client_id` -> Client, optional `site_id` -> Site |
Most-used client fields: `id`, `name` (required), `emailaddress`, `phonenumber`,
`website`, `accountmanager_id`, `inactive`, `client_to_invoice`, `toplevel_id`.
Most-used contact fields: `name` (required), `client_id` (required), `site_id`,
`emailaddress`, `jobtitle`, `isimportantcontact`, `inactive`.
Clients are classified (Customer, Prospect, Lead, Partner, Vendor) via custom fields
and categories rather than a built-in type enum.
See [references/fields.md](references/fields.md) for the complete field reference for clients, sites, contacts, and client types.
## Client Hierarchy
HaloPSA supports parent-child client relationships via two separate fields:
- `client_to_invoice` — the client that receives the invoice
- `toplevel_id` — the top-level parent in the reporting hierarchy
Set both when a child should both roll up for reporting and bill to the parent.
### Hierarchy Use Cases
- **Franchise operations** - Parent company, individual locations
- **Multi-site organizations** - Headquarters with branch offices
- **Billing consolidation** - Invoice parent, service children
## API Patterns
- **Create and update use the same call.** `POST /api/Client` (also `/api/Site`,
`/api/Users`) creates when no `id` is present and updates when `id` is supplied.
There is no `PUT`/`PATCH`.
- **The body is always a JSON array**, even for a single record. This is also how
you batch multiple creates/updates in one request.
- **Updates are partial** — send only `id` plus the fields you are changing.
- **Related data is opt-in** on single-record GETs: `GET /api/Client/123?includesites=true&includeusers=true`.
- **Pagination and sorting** use `page_no`, `page_size`, `order`, `orderdesc`.
See [references/api.md](references/api.md) for full request/response examples covering client, site, and contact create/search/update.
## Common Workflows
### Client Onboarding
1. **Create client record**
- Set name and contact information
- Assign account manager
- Configure billing settings
2. **Create primary site**
- Add main location address
- Set as main_site
3. **Create primary contact**
- Add key stakeholder
- Set as important contact
- Verify email address
4. **Set up contract**
- Link to client
- Define service levels
- Configure billing
5. **Deploy assets** (if applicable)
- Link devices to client
- Associate with site
Search for an existing record by name or email before each create step — HaloPSA
does not enforce uniqueness on client names or contact emails across clients.
See [references/examples.md](references/examples.md) for a contact-onboarding
routine that does the duplicate check.
### Client Deactivation
When a client churns:
1. **Mark client inactive**
```json
[{ "id": 123, "inactive": true }]
```
2. **Close open tickets**
- Resolve or cancel pending tickets
- Document reason
3. **End contracts**
- Update contract end dates
- Process final billing
4. **Update assets**
- Return or reassign devices
- Update RMM status
Never delete a client that has related records — HaloPSA returns 409 and
deactivation is the supported path.
## Error Handling
| Code | Message | Resolution |
|------|---------|------------|
| 400 | Name is required | Client must have a name |
| 400 | Invalid client_id | Parent client doesn't exist |
| 400 | Duplicate email | Contact email already in use |
| 404 | Client not found | Verify client ID |
| 409 | Cannot delete - has related records | Deactivate instead |
See [references/examples.md](references/examples.md) for client validation and
duplicate-detection helpers.
## Data Quality Queries
### Find clients without contacts
```http
GET /api/Client?hasusers=false&inactive=false
```
### Find contacts without email
```http
GET /api/Users?emailaddress=null&inactive=false
```
Duplicate client names are not detectable via a query parameter — fetch and
normalize names client-side (see [references/examples.md](references/examples.md)).
## Best Practices
1. **Standardize naming** - Use consistent company name formats
2. **Verify before creating** - Always search first to prevent duplicates
3. **Use classifications** - Categorize clients for reporting
4. **Track account managers** - Assign for accountability
5. **Keep contacts current** - Deactivate departed employees
6. **Document relationships** - Use notes for key account information
7. **Set up hierarchy correctly** - Proper parent-child for billing
## Related Skills
- [HaloPSA Tickets](../tickets/SKILL.md) - Service tickets for clients
- [HaloPSA Contracts](../contracts/SKILL.md) - Service agreements
- [HaloPSA Assets](../assets/SKILL.md) - Client assets
- [HaloPSA API Patterns](../api-patterns/SKILL.md) - Authentication and queries
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!