Hudu Companies Management
Overview
Companies are the foundational entity in Hudu, representing clients, vendors, or internal entities. All documentation, assets, passwords, articles, and websites are associated with a company. In Hudu, the "Company" label is customizable per instance -- some MSPs rename it to "Organization" or "Client" -- but the API endpoint is always /api/v1/companies.
Key Concepts
Company Types
Unlike IT Glue, Hudu does not enforce built-in company types. Companies are typically organized using custom fields or naming conventions. Common patterns MSPs use:
| Pattern | Description | Example | |---------|-------------|---------| | Active Client | Currently serviced customer | Standard operational state | | Prospect | Potential client | Pre-sales documentation | | Vendor | Product/service supplier | Software vendors | | Internal | Your own MSP | Internal documentation | | Former Client | Previously serviced | Historical records |
Company Hierarchy
Companies can have parent/child relationships for multi-location or multi-division clients:
Parent Company (Acme Holdings)
+-- Child: Acme East Division
+-- Child: Acme West Division
+-- Child: Acme International
PSA Integration
Companies can be matched to PSA records using the id_in_integration and integration_slug fields, enabling cross-platform lookups between Hudu and tools like ConnectWise Manage, Autotask, or HaloPSA.
Field Reference
Core Fields
| Field | Type | Required | Description | |-------|------|----------|-------------| | id | integer | System | Auto-generated unique identifier | | name | string | Yes | Company name | | nickname | string | No | Short name or abbreviation | | company_type | string | No | Type classification | | address_line_1 | string | No | Street address line 1 | | address_line_2 | string | No | Street address line 2 | | city | string | No | City | | state | string | No | State/province | | zip | string | No | Postal code | | country_name | string | No | Country | | phone_number | string | No | Phone number | | fax_number | string | No | Fax number | | website | string | No | Company website URL | | notes | string | No | Rich text notes |
Integration Fields
| Field | Type | Description | |-------|------|-------------| | id_in_integration | integer | PSA system company ID | | integration_slug | string | PSA integration identifier |
Relationship Fields
| Field | Type | Description | |-------|------|-------------| | parent_company_id | integer | Parent company ID | | parent_company_name | string | Parent company name (read-only) |
Metadata Fields
| Field | Type | Description | |-------|------|-------------| | created_at | datetime | Creation timestamp | | updated_at | datetime | Last update timestamp | | slug | string | URL-friendly identifier | | object_type | string | Always "Company" |
API Patterns
List Companies
GET /api/v1/companies
x-api-key: YOUR_API_KEY
Content-Type: application/json
With Filters:
GET /api/v1/companies?name=Acme
GET /api/v1/companies?city=Springfield
GET /api/v1/companies?state=IL
GET /api/v1/companies?id_in_integration=12345
GET /api/v1/companies?search=acme
With Pagination:
GET /api/v1/companies?page=1
GET /api/v1/companies?page=2
Get Single Company
GET /api/v1/companies/123
x-api-key: YOUR_API_KEY
Create Company
POST /api/v1/companies
Content-Type: application/json
x-api-key: YOUR_API_KEY
{
"company": {
"name": "New Client Corporation",
"nickname": "NCC",
"company_type": "Customer",
"address_line_1": "123 Main Street",
"city": "Portland",
"state": "OR",
"zip": "97201",
"phone_number": "555-123-4567",
"website": "https://newclient.com",
"notes": "Onboarded February 2026. Primary contact: John Smith."
}
}
Update Company
PUT /api/v1/companies/123
Content-Type: application/json
x-api-key: YOUR_API_KEY
{
"company": {
"nickname": "NCC-UPDATED",
"notes": "Updated: New primary contact is Jane Doe (555-987-6543)."
}
}
Delete Company
DELETE /api/v1/companies/123
x-api-key: YOUR_API_KEY
Warning: Deleting a company removes all associated resources (assets, passwords, articles, etc.). Requires DELETE permission on the API key.
Archive / Unarchive Company
PUT /api/v1/companies/123/archive
x-api-key: YOUR_API_KEY
PUT /api/v1/companies/123/unarchive
x-api-key: YOUR_API_KEY
Search by PSA Integration ID
GET /api/v1/companies?id_in_integration=12345
Common Workflows
New Client Onboarding
- Create company with basic info (name, address, phone, website)
- Set integration ID to link with PSA
- Add notes for quick reference (primary contact, contract info)
- Create initial assets (servers, workstations, network devices)
- Document passwords for the company
- Create articles (network overview, procedures)
- Add website records for monitoring
async function onboardClient(clientData) {
// Step 1: Create company
const company = await createCompany({
name: clientData.companyName,
nickname: clientData.nickname,
company_type: 'Customer',
address_line_1: clientData.address,
city: clientData.city,
state: clientData.state,
zip: clientData.zip,
phone_number: clientData.phone,
website: clientData.website,
notes: `Onboarded: ${new Date().toLocaleDateString()}\nPrimary contact: ${clientData.primaryContact}`
});
// Step 2: Link to PSA
if (clientData.psaId) {
await updateCompany(company.id, {
id_in_integration: clientData.psaId
});
}
return company;
}
Client Offboarding
- Review and export critical documentation if needed
- Archive passwords (do not delete for audit purposes)
- Archive the company instead of deleting
- Add offboarding notes with date and reason
async function offboardClient(companyId, reason) {
// Add offboarding notes
await updateCompany(companyId, {
notes: `ARCHIVED: ${new Date().toLocaleDateString()} - ${reason}`
});
// Archive the company
await archiveCompany(companyId);
}
PSA Sync Verification
async function verifyPsaSync() {
const companies = await fetchAllCompanies();
const syncStatus = {
synced: [],
unsynced: [],
mismatched: []
};
for (const company of companies) {
if (!company.id_in_integration) {
syncStatus.unsynced.push(company);
} else {
const psaCompany = await lookupPsaCompany(company.id_in_integration);
if (psaCompany) {
syncStatus.synced.push(company);
} else {
syncStatus.mismatched.push(company);
}
}
}
return syncStatus;
}
Bulk Company Report
async function generateCompanyReport() {
const companies = await fetchAllCompanies();
return companies.map(company => ({
name: company.name,
nickname: company.nickname,
city: company.city,
state: company.state,
psaSynced: !!company.id_in_integration,
hasWebsite: !!company.website,
createdAt: company.created_at,
updatedAt: company.updated_at
}));
}
Error Handling
Common API Errors
| Code | Message | Resolution | |------|---------|------------| | 400 | Name can't be blank | Provide company name | | 400 | Name has already been taken | Use unique name | | 401 | Invalid API key | Check HUDU_API_KEY | | 404 | Company not found | Verify company ID and HUDU_BASE_URL | | 422 | Validation failed | Check required fields |
Validation Errors
| Error | Cause | Fix | |-------|-------|-----| | Name required | Missing name field | Add name to request body | | Name not unique | Duplicate company name | Use a different name | | Invalid parent ID | Non-existent parent co