agentsclimarketplace

Maintainx reference architecture

Skill jeremylongshore/claude-code-plugins-plus-skills/skills/.curated/maintainx-reference-architecture

425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill maintainx-reference-architecture

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

'Production-grade architecture patterns for MaintainX integrations.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

10.1 KB, as published. Nobody here has run it

MaintainX Reference Architecture

Overview

Production-grade architecture patterns for building scalable, maintainable integrations between MaintainX and enterprise systems (ERP, SCADA, data warehouses).

Prerequisites

  • Understanding of distributed systems
  • Cloud platform experience (GCP, AWS, or Azure)
  • MaintainX API familiarity

Instructions

Step 1: Event-Driven Sync Architecture

The recommended architecture for most MaintainX integrations. Uses webhooks for real-time updates and scheduled jobs for reconciliation.

MaintainX API ──webhook──→ Cloud Run ──→ Pub/Sub ──→ Cloud Functions
                                              │
                                              ├──→ BigQuery (analytics)
                                              ├──→ ERP System (SAP, Oracle)
                                              └──→ Notification Service
// src/architecture/event-driven.ts
import express from 'express';
import { PubSub } from '@google-cloud/pubsub';

const app = express();
const pubsub = new PubSub();
const topic = pubsub.topic('maintainx-events');

// Webhook receiver publishes to Pub/Sub
app.post('/webhooks/maintainx', async (req, res) => {
  const { event, data } = req.body;

  await topic.publishMessage({
    data: Buffer.from(JSON.stringify({ event, data })),
    attributes: { event, resourceId: String(data.id) },
  });

  res.status(200).json({ status: 'queued' });
});

// Subscriber processes events asynchronously
const subscription = pubsub.subscription('maintainx-events-sub');
subscription.on('message', async (message) => {
  const { event, data } = JSON.parse(message.data.toString());

  switch (event) {
    case 'workorder.completed':
      await syncToERP(data);
      await updateAnalytics(data);
      break;
    case 'workorder.created':
      if (data.priority === 'HIGH') {
        await sendUrgentNotification(data);
      }
      break;
  }

  message.ack();
});

Step 2: Bi-Directional Sync Gateway

For integrating MaintainX with ERP systems (SAP, Oracle) where changes flow both ways.

ERP (SAP/Oracle) ←──→ Sync Gateway ←──→ MaintainX API
                          │
                    Conflict Resolution
                    + Audit Trail
                    + Sync State DB
// src/architecture/sync-gateway.ts

interface SyncRecord {
  externalId: string;     // ERP system ID
  maintainxId: number;    // MaintainX ID
  lastSyncAt: string;
  syncDirection: 'inbound' | 'outbound' | 'bidirectional';
  hash: string;           // Content hash for change detection
}

class SyncGateway {
  constructor(
    private maintainx: MaintainXClient,
    private erp: ERPClient,
    private db: SyncStateDB,
  ) {}

  // MaintainX → ERP
  async syncToERP(workOrder: any) {
    const existing = await this.db.findByMaintainxId(workOrder.id);

    if (existing && this.hash(workOrder) === existing.hash) {
      return; // No change, skip
    }

    const erpRecord = this.mapToERP(workOrder);
    if (existing) {
      await this.erp.update(existing.externalId, erpRecord);
    } else {
      const created = await this.erp.create(erpRecord);
      await this.db.create({
        externalId: created.id,
        maintainxId: workOrder.id,
        lastSyncAt: new Date().toISOString(),
        syncDirection: 'outbound',
        hash: this.hash(workOrder),
      });
    }
  }

  // ERP → MaintainX
  async syncFromERP(erpRecord: any) {
    const existing = await this.db.findByExternalId(erpRecord.id);
    const woData = this.mapFromERP(erpRecord);

    if (existing) {
      await this.maintainx.updateWorkOrder(existing.maintainxId, woData);
    } else {
      const created = await this.maintainx.createWorkOrder(woData);
      await this.db.create({
        externalId: erpRecord.id,
        maintainxId: created.id,
        lastSyncAt: new Date().toISOString(),
        syncDirection: 'inbound',
        hash: this.hash(created),
      });
    }
  }

  private mapToERP(wo: any) {
    return {
      title: wo.title,
      status: this.mapStatus(wo.status),
      priority: wo.priority,
      completedAt: wo.completedAt,
    };
  }

  private mapFromERP(erp: any) {
    return {
      title: erp.description,
      priority: erp.urgency === 'HIGH' ? 'HIGH' : 'MEDIUM',
    };
  }

  private mapStatus(status: string) {
    const map: Record<string, string> = {
      OPEN: 'PLANNED',
      IN_PROGRESS: 'ACTIVE',
      COMPLETED: 'FINISHED',
      CLOSED: 'ARCHIVED',
    };
    return map[status] || 'UNKNOWN';
  }

  private hash(obj: any): string {
    return require('crypto').createHash('md5')
      .update(JSON.stringify(obj)).digest('hex');
  }
}

Step 3: Analytics Data Pipeline

MaintainX API ──scheduled──→ Cloud Functions ──→ BigQuery
                                                    │
                                              Looker / Metabase
                                                    │
                                              KPI Dashboards:
                                              - MTTR (Mean Time to Repair)
                                              - PM Compliance %
                                              - Work Order Backlog
                                              - Asset Downtime
// src/architecture/analytics-pipeline.ts

interface MaintenanceKPIs {
  mttr: number;           // Mean Time to Repair (hours)
  pmCompliance: number;   // Preventive Maintenance compliance %
  backlog: number;        // Open work orders count
  completionRate: number; // Orders completed / orders created
}

async function calculateKPIs(client: MaintainXClient): Promise<MaintenanceKPIs> {
  const completed = await paginate(
    (cursor) => client.getWorkOrders({ status: 'COMPLETED', limit: 100, cursor }),
    'workOrders',
  );

  const open = await paginate(
    (cursor) => client.getWorkOrders({ status: 'OPEN', limit: 100, cursor }),
    'workOrders',
  );

  // MTTR: Average time from OPEN to COMPLETED
  const repairTimes = completed
    .filter((wo: any) => wo.createdAt && wo.completedAt)
    .map((wo: any) => {
      const created = new Date(wo.createdAt).getTime();
      const completed = new Date(wo.completedAt).getTime();
      return (completed - created) / 3600000; // hours
    });

  const mttr = repairTimes.length > 0
    ? repairTimes.reduce((a: number, b: number) => a + b, 0) / repairTimes.length
    : 0;

  // PM Compliance
  const pmOrders = completed.filter((wo: any) =>
    wo.categories?.includes('PREVENTIVE'),
  );
  const allPM = [...pmOrders, ...open.filter((wo: any) =>
    wo.categories?.includes('PREVENTIVE'),
  )];
  const pmCompliance = allPM.length > 0 ? (pmOrders.length / allPM.length) * 100 : 100;

  return {
    mttr: Math.round(mttr * 10) / 10,
    pmCompliance: Math.round(pmCompliance),
    backlog: open.length,
    completionRate: completed.length / (completed.length + open.length) * 100,
  };
}

Step 4: Multi-Site Architecture

Site A (Plant)           Site B (Warehouse)        Site C (Office)
  └── Local Agent           └── Local Agent            └── Local Agent
        │                         │                          │
        └─────────── Central Hub (Cloud Run) ────────────────┘
                          │
                    MaintainX API
                    (Org-level access)
// Central hub routing requests by site
const siteConfigs = {
  'plant-austin': { orgId: 'org-1', apiKey: process.env.MX_KEY_PLANT },
  'warehouse-dallas': { orgId: 'org-2', apiKey: process.env.MX_KEY_WAREHOUSE },
  'office-houston': { orgId: 'org-3', apiKey: process.env.MX_KEY_OFFICE },
};

function getClientForSite(siteId: string): MaintainXClient {
  const config = siteConfigs[siteId as keyof typeof siteConfigs];
  if (!config) throw new Error(`Unknown site: ${siteId}`);
  return new MaintainXClient(config.apiKey, config.orgId);
}

Output

  • Event-driven architecture with Pub/Sub for decoupled processing
  • Bi-directional sync gateway with conflict resolution and audit trail
  • Analytics pipeline calculating maintenance KPIs (MTTR, PM compliance)
  • Multi-site architecture with per-site API key isolation

Error Handling

PatternFailure ModeMitigation
Event-drivenPub/Sub delivery failureDead letter queue, retry policy
Bi-directional syncConflict on same recordLast-write-wins or manual resolution
Analytics pipelineIncomplete data fetchRetry with backfill, validate counts
Multi-siteOne site API key expiredIndependent health checks per site

Resources

Next Steps

For multi-environment setup, see maintainx-multi-env-setup.

Examples

SCADA integration (pulling sensor data into MaintainX work orders):

// Auto-create work order when equipment sensor exceeds threshold
async function handleSensorAlert(sensorId: string, value: number, threshold: number) {
  const asset = await findAssetBySensorId(sensorId);

  await client.createWorkOrder({
    title: `Sensor Alert: ${asset.name} - ${sensorId} exceeded threshold`,
    description: `Value: ${value} (threshold: ${threshold}). Auto-generated from SCADA.`,
    priority: value > threshold * 1.5 ? 'HIGH' : 'MEDIUM',
    assetId: asset.maintainxId,
    categories: ['CORRECTIVE'],
  });
}

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.