Help us improve
Share bugs, ideas, or general feedback.
From hudu
Manages Hudu companies (clients/organizations): create, search, update, archive; covers fields, PSA integrations, parent/child hierarchies, and related assets/passwords/articles.
npx claudepluginhub wyre-technology/msp-claude-plugins --plugin huduHow this skill is triggered — by the user, by Claude, or both
Slash command
/hudu:companiesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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`.
Manages ConnectWise PSA company records via API: create, update, search; handle types, statuses, sites/locations, custom fields, relationships.
Guides management of Autotask CRM entities: companies, contacts, sites/locations, and opportunities. Useful for MSP account management, client onboarding, and REST API usage.
Manages IT Glue organizations: create, search, update, and handle client documentation including types, statuses, parent/child relationships, PSA sync, quick notes, and related configs, contacts, passwords.
Share bugs, ideas, or general feedback.
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.
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 |
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
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 | 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 |
| Field | Type | Description |
|---|---|---|
id_in_integration | integer | PSA system company ID |
integration_slug | string | PSA integration identifier |
| Field | Type | Description |
|---|---|---|
parent_company_id | integer | Parent company ID |
parent_company_name | string | Parent company name (read-only) |
| Field | Type | Description |
|---|---|---|
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
slug | string | URL-friendly identifier |
object_type | string | Always "Company" |
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 /api/v1/companies/123
x-api-key: YOUR_API_KEY
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."
}
}
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 /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.
PUT /api/v1/companies/123/archive
x-api-key: YOUR_API_KEY
PUT /api/v1/companies/123/unarchive
x-api-key: YOUR_API_KEY
GET /api/v1/companies?id_in_integration=12345
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;
}
async function offboardClient(companyId, reason) {
// Add offboarding notes
await updateCompany(companyId, {
notes: `ARCHIVED: ${new Date().toLocaleDateString()} - ${reason}`
});
// Archive the company
await archiveCompany(companyId);
}
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;
}
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
}));
}
| 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 |
| 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 |
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;
}
}
id_in_integration for cross-platform lookups