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.
Anti-triggers
- The client record of record — a Hudu company scopes documentation.
Contracts, contacts, and service history live in the PSA; use
autotask-crm,connectwise-psa-companies, orhalopsa-clients. Hudu'sid_in_integrationfield holds the PSA's ID precisely because the two are different records. - The same client in IT Glue — IT Glue calls these Organizations; use
itglue-api-patternsfor its equivalent surface. - The monitored client container — an RMM organization or site is a
monitoring scope, not documentation; use
ninjaone-organizations,atera-customers, ordatto-rmm-sites. - The client's licence or billing entity — use
pax8-companies,sherweb-customers, orqbo-customers.
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 company | Verify parent_company_id |
Error Recovery Pattern
async function safeCreateCompany(data) {
try {
return await createCompany(data);
} catch (error) {
if (error.status === 422 && error.message?.includes('already been taken')) {
// Company exists - find and return it
const existing = await findCompanyByName(data.name);
return existing;
}
if (error.status === 401) {
throw new Error('API key invalid or expired. Check HUDU_API_KEY.');
}
throw error;
}
}
Best Practices
- Use descriptive names - Include location or identifier if needed for uniqueness
- Set nicknames - Short abbreviations for quick reference
- Maintain notes - Keep emergency contact info and contract details readily available
- Link to PSA - Always set
id_in_integrationfor cross-platform lookups - Use parent/child - Organize multi-location or division clients
- Archive, don't delete - Preserve historical documentation
- Include address info - Useful for dispatch and site visit planning
- Document website - Track the company's primary website URL
Related Skills
- Hudu Assets - Asset management for companies
- Hudu Articles - Knowledge base articles
- Hudu Passwords - Credential storage
- Hudu Websites - Website monitoring
- Hudu API Patterns - API reference