Skip to content
maintainx-core-workflow-b logo

Maintainx Core Workflow B

maintainx-core-workflow-b

Execute MaintainX secondary workflow: Asset and Location management. Use when managing equipment assets, organizing locations/facilities, building asset hierarchies, and tracking equipment maintenance history. Trigger with phrases like "maintainx asset", "maintainx location", "equipment tracking"...

SKILL.md

Full skill instructions

MaintainX Core Workflow B: Asset & Location Management

Overview

Manage equipment assets and locations in MaintainX. Assets represent equipment that requires maintenance; locations organize your facilities.

Prerequisites

  • Completed maintainx-install-auth setup
  • Understanding of asset hierarchy concepts
  • MaintainX account with asset management permissions

Asset Hierarchy Model

Organization
├── Location: Main Plant
│   ├── Sub-Location: Building A
│   │   ├── Asset: HVAC Unit A1
│   │   │   └── Sub-Asset: Compressor
│   │   └── Asset: Conveyor Line 1
│   └── Sub-Location: Building B
│       └── Asset: Boiler System
└── Location: Warehouse
    └── Asset: Forklift Fleet
        ├── Sub-Asset: Forklift #1
        └── Sub-Asset: Forklift #2

Instructions

Step 1: Query Locations

// src/​workflows/​asset-location.ts
import { MaintainXClient } from '../​api/​maintainx-client';

interface Location {
  id: string;
  name: string;
  address?: string;
  parentId?: string;
  children?: Location[];
}

// Get all locations
async function getAllLocations(client: MaintainXClient): Promise<Location[]> {
  const allLocations: Location[] = [];
  let cursor: string | undefined;

  do {
    const response = await client.getLocations({ cursor, limit: 100 });
    allLocations.push(...response.locations);
    cursor = response.nextCursor || undefined;
  } while (cursor);

  return allLocations;
}

// Build location hierarchy tree
function buildLocationTree(locations: Location[]): Location[] {
  const locationMap = new Map<string, Location>();
  const rootLocations: Location[] = [];

  // First pass: create map
  locations.forEach(loc => {
    locationMap.set(loc.id, { ...loc, children: [] });
  });

  // Second pass: build tree
  locations.forEach(loc => {
    const node = locationMap.get(loc.id)!;
    if (loc.parentId && locationMap.has(loc.parentId)) {
      const parent = locationMap.get(loc.parentId)!;
      parent.children!.push(node);
    } else {
      rootLocations.push(node);
    }
  });

  return rootLocations;
}

// Print location tree
function printLocationTree(locations: Location[], indent = 0) {
  const prefix = '  '.repeat(indent);
  locations.forEach(loc => {
    console.log(`${prefix}├── ${loc.name} (${loc.id})`);
    if (loc.children && loc.children.length > 0) {
      printLocationTree(loc.children, indent + 1);
    }
  });
}

// Usage
async function displayLocationHierarchy(client: MaintainXClient) {
  console.log('=== Location Hierarchy ===\n');
  const locations = await getAllLocations(client);
  const tree = buildLocationTree(locations);
  printLocationTree(tree);
}

Step 2: Query Assets

interface Asset {
  id: string;
  name: string;
  serialNumber?: string;
  model?: string;
  manufacturer?: string;
  status: 'OPERATIONAL' | 'NON_OPERATIONAL' | 'DECOMMISSIONED';
  locationId?: string;
  location?: Location;
  parentAssetId?: string;
  customFields?: Record<string, any>;
  createdAt: string;
  updatedAt: string;
}

// Get all assets
async function getAllAssets(client: MaintainXClient): Promise<Asset[]> {
  const allAssets: Asset[] = [];
  let cursor: string | undefined;

  do {
    const response = await client.getAssets({ cursor, limit: 100 });
    allAssets.push(...response.assets);
    cursor = response.nextCursor || undefined;
  } while (cursor);

  return allAssets;
}

// Get assets by location
async function getAssetsByLocation(
  client: MaintainXClient,
  locationId: string
): Promise<Asset[]> {
  const response = await client.getAssets({ locationId, limit: 100 });
  return response.assets;
}

// Get asset details
async function getAssetDetails(
  client: MaintainXClient,
  assetId: string
): Promise<Asset> {
  return client.getAsset(assetId);
}

Step 3: Asset Analysis

interface AssetAnalysis {
  totalAssets: number;
  byStatus: Record<string, number>;
  byLocation: Record<string, number>;
  byManufacturer: Record<string, number>;
  noLocation: Asset[];
}

async function analyzeAssets(client: MaintainXClient): Promise<AssetAnalysis> {
  const assets = await getAllAssets(client);

  const analysis: AssetAnalysis = {
    totalAssets: assets.length,
    byStatus: {},
    byLocation: {},
    byManufacturer: {},
    noLocation: [],
  };

  assets.forEach(asset => {
    // Count by status
    const status = asset.status || 'UNKNOWN';
    analysis.byStatus[status] = (analysis.byStatus[status] || 0) + 1;

    // Count by location
    if (asset.location?.name) {
      const loc = asset.location.name;
      analysis.byLocation[loc] = (analysis.byLocation[loc] || 0) + 1;
    } else {
      analysis.noLocation.push(asset);
    }

    // Count by manufacturer
    if (asset.manufacturer) {
      analysis.byManufacturer[asset.manufacturer] =
        (analysis.byManufacturer[asset.manufacturer] || 0) + 1;
    }
  });

  return analysis;
}

// Print analysis report
function printAssetReport(analysis: AssetAnalysis) {
  console.log('=== Asset Analysis Report ===\n');
  console.log(`Total Assets: ${analysis.totalAssets}\n`);

  console.log('By Status:');
  Object.entries(analysis.byStatus).forEach(([status, count]) => {
    console.log(`  ${status}: ${count}`);
  });

  console.log('\nBy Location:');
  Object.entries(analysis.byLocation)
    .sort((a, b) => b[1] - a[1])
    .slice(0, 10)
    .forEach(([loc, count]) => {
      console.log(`  ${loc}: ${count}`);
    });

  console.log('\nBy Manufacturer:');
  Object.entries(analysis.byManufacturer)
    .sort((a, b) => b[1] - a[1])
    .slice(0, 10)
    .forEach(([mfr, count]) => {
      console.log(`  ${mfr}: ${count}`);
    });

  if (analysis.noLocation.length > 0) {
    console.log(`\nAssets without location: ${analysis.noLocation.length}`);
  }
}

Step 4: Asset Work Order History

// Get maintenance history for an asset
async function getAssetMaintenanceHistory(
  client: MaintainXClient,
  assetId: string
) {
  const workOrders = await client.getWorkOrders({
    assetId,
    limit: 100,
  });

  // Analyze work order history
  const history = {
    total: workOrders.workOrders.length,
    completed: workOrders.workOrders.filter(wo => wo.status === 'DONE').length,
    open: workOrders.workOrders.filter(wo => wo.status === 'OPEN').length,
    inProgress: workOrders.workOrders.filter(wo => wo.status === 'IN_PROGRESS').length,
    recentWorkOrders: workOrders.workOrders.slice(0, 5),
  };

  return history;
}

// Generate asset report card
async function generateAssetReportCard(
  client: MaintainXClient,
  assetId: string
) {
  const asset = await getAssetDetails(client, assetId);
  const history = await getAssetMaintenanceHistory(client, assetId);

  console.log('=== Asset Report Card ===\n');
  console.log(`Name: ${asset.name}`);
  console.log(`ID: ${asset.id}`);
  console.log(`Status: ${asset.status}`);
  console.log(`Serial Number: ${asset.serialNumber || 'N/​A'}`);
  console.log(`Manufacturer: ${asset.manufacturer || 'N/​A'}`);
  console.log(`Model: ${asset.model || 'N/​A'}`);
  console.log(`Location: ${asset.location?.name || 'Not assigned'}`);
  console.log(`\nMaintenance History:`);
  console.log(`  Total Work Orders: ${history.total}`);
  console.log(`  Completed: ${history.completed}`);
  console.log(`  Open: ${history.open}`);
  console.log(`  In Progress: ${history.inProgress}`);

  if (history.recentWorkOrders.length > 0) {
    console.log('\nRecent Work Orders:');
    history.recentWorkOrders.forEach(wo => {
      console.log(`  - ${wo.title} (${wo.status})`);
    });
  }

  return { asset, history };
}

Step 5: Location-Based Asset View

// Get assets organized by location
async function getAssetsGroupedByLocation(client: MaintainXClient) {
  const locations = await getAllLocations(client);
  const assets = await getAllAssets(client);

  const assetsByLocation: Map<string, Asset[]> = new Map();

  // Group assets by location
  assets.forEach(asset => {
    const locId = asset.locationId || 'UNASSIGNED';
    if (!assetsByLocation.has(locId)) {
      assetsByLocation.set(locId, []);
    }
    assetsByLocation.get(locId)!.push(asset);
  });

  // Create location map for names
  const locationMap = new Map(
    locations.map(loc => [loc.id, loc.name])
  );

  // Print organized view
  console.log('=== Assets by Location ===\n');
  assetsByLocation.forEach((locAssets, locId) => {
    const locName = locationMap.get(locId) || locId;
    console.log(`\n${locName} (${locAssets.length} assets):`);
    locAssets.forEach(asset => {
      const status = asset.status === 'OPERATIONAL' ? '[OK]' : '[!]';
      console.log(`  ${status} ${asset.name}`);
    });
  });

  return assetsByLocation;
}

Step 6: Preventive Maintenance Planning

interface PMSchedule {
  assetId: string;
  assetName: string;
  lastMaintenanceDate?: Date;
  nextDueDate: Date;
  frequency: string; // e.g., "30 days", "quarterly"
  tasks: string[];
}

// Plan preventive maintenance based on asset types
function generatePMSchedule(assets: Asset[]): PMSchedule[] {
  const schedules: PMSchedule[] = [];

  assets.forEach(asset => {
    // Skip non-operational assets
    if (asset.status !== 'OPERATIONAL') return;

    // Example PM schedules based on asset type (infer from name)
    const name = asset.name.toLowerCase();

    if (name.includes('hvac') || name.includes('air')) {
      schedules.push({
        assetId: asset.id,
        assetName: asset.name,
        nextDueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        frequency: '30 days',
        tasks: [
          'Replace air filters',
          'Check refrigerant levels',
          'Inspect belts and pulleys',
          'Clean coils',
          'Test thermostat',
        ],
      });
    } else if (name.includes('pump')) {
      schedules.push({
        assetId: asset.id,
        assetName: asset.name,
        nextDueDate: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000),
        frequency: '90 days',
        tasks: [
          'Check seal condition',
          'Inspect impeller',
          'Verify flow rates',
          'Lubricate bearings',
        ],
      });
    } else if (name.includes('conveyor')) {
      schedules.push({
        assetId: asset.id,
        assetName: asset.name,
        nextDueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        frequency: '7 days',
        tasks: [
          'Inspect belt tension',
          'Check motor operation',
          'Lubricate rollers',
          'Clean sensors',
        ],
      });
    }
  });

  return schedules;
}

// Create work orders from PM schedule
async function createPMWorkOrders(
  client: MaintainXClient,
  schedules: PMSchedule[]
) {
  const created = [];

  for (const schedule of schedules) {
    const workOrder = await client.createWorkOrder({
      title: `Scheduled PM - ${schedule.assetName}`,
      description: `
## Preventive Maintenance
Frequency: ${schedule.frequency}

## Tasks
${schedule.tasks.map(t => `- [ ] ${t}`).join('\n')}
      `,
      priority: 'MEDIUM',
      assetId: schedule.assetId,
      dueDate: schedule.nextDueDate.toISOString(),
    });
    created.push(workOrder);
  }

  return created;
}

Output

  • Complete location hierarchy view
  • Asset inventory analysis
  • Asset maintenance history
  • Location-based asset groupings
  • Preventive maintenance schedules

Error Handling

ErrorCauseSolution
404 Not FoundInvalid asset/​location IDVerify ID exists
Empty ResultsNo data or wrong filterCheck query parameters
Pagination issuesMissing cursor handlingUse pagination helper
Permission deniedInsufficient accessVerify user permissions

Asset Status Reference

StatusDescriptionAction
OPERATIONALWorking normallySchedule PM
NON_OPERATIONALNot functioningCreate repair WO
DECOMMISSIONEDRetired from useArchive/​remove

Resources

Next Steps

For troubleshooting common issues, see maintainx-common-errors.

More Productivity & Planning skills

brainstorming logo
Productivity & Planning

brainstorming

Structured design dialogue that validates ideas before implementation begins.

295.4K 385.7K
View
ui-ux-pro-max logo
Productivity & Planning

ui-ux-pro-max

Comprehensive design intelligence for web and mobile UI/UX across 10 technology stacks.

133.1K 383.9K
View
writing-plans logo
Productivity & Planning

writing-plans

Comprehensive implementation plans for multi-step tasks, breaking down specs into bite-sized, testable steps.

295.4K 268K
View
using-superpowers logo
Productivity & Planning

using-superpowers

Introduction to the obra skills system with mandatory skill invocation rules and best practices.

295.4K 259.8K
View
executing-plans logo
Productivity & Planning

executing-plans

Execute a written implementation plan with critical review and task checkpoints.

295.4K 229.1K
View
dispatching-parallel-agents logo
Productivity & Planning

dispatching-parallel-agents

Delegate independent tasks to specialized agents working concurrently with isolated context.

295.4K 206.3K
View
using-git-worktrees logo
Productivity & Planning

using-git-worktrees

Isolated git worktrees with smart directory selection and safety verification.

295.4K 205.2K
View
webapp-testing logo
Productivity & Planning

webapp-testing

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

179.7K 170.6K
View
content-strategy logo
Productivity & Planning

content-strategy

Plan searchable and shareable content that drives traffic, builds authority, and generates leads.

53.3K 150.9K
View
repo-intake-and-plan logo
Productivity & Planning

repo-intake-and-plan

README-first repository scanner that extracts commands and classifies reproduction candidates without executing them.

497 139.6K
View
marketing-ideas logo
Productivity & Planning

marketing-ideas

Brainstorm and prioritize marketing strategies tailored to your SaaS stage, budget, and goals.

53.3K 137.2K
View
site-architecture logo
Productivity & Planning

site-architecture

Plan and optimize your website's page hierarchy, navigation, URL structure, and internal linking.

53.3K 112.8K
View

Productivity AI tools

Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View
SaveDay logo
Productivity

SaveDay

Capture, organize, and utilize your knowledge effortlessly.

Free
View
A
Productivity

Any Summary

Instant Summaries of Audio & Video Interviews with AnySummary

Freemium
View
M
Productivity

Map This

Transform PDFs into engaging mind maps.

Freemium
View
ChatPDF logo
Productivity

ChatPDF

Chat with any PDF instantly

Free
View
I
Productivity

intellisay

Create an optimal daily plan using your voice

Paid
View
A
Productivity

Aurora AI

A productivity platform to centralize organizational knowledge and workflows with contextual AI assistance.

Paid
View
AskYourPDF logo
Productivity

AskYourPDF

AskYourPDF Pricing Plans: Tailored to Your Needs

Free
View