Skip to navigation
Maintainx-webhooks-events
27.08.26
maintainx-webhooks-events 'Implement MaintainX webhook handling and event-driven integrations. v1.11.0 Jeremy Longshore MIT Allowed Tools ReadWriteEditBash(curl:*)Bash(npm:*) Provided by Plugin maintainx-pack Claude Code skill pack for MaintainX CMMS (24 skills) saas packs v1.11.0 View Plugin Installation This skill is included in the maintainx-pack plugin: /plugin install maintainx-pack@claude-code-plugins-plus Click to copy Instructions MaintainX Webhooks & Events Overview Build real-time integrations with MaintainX using webhooks for work order updates, asset changes, and maintenance notifications. MaintainX fires webhook events when key resources change. Prerequisites MaintainX account with API access HTTPS endpoint accessible from the internet (ngrok for local dev) MAINTAINXAPIKEY environment variable configured Instructions Step 1: Register a Webhook curl -X POST https://api.getmaintainx.com/v1/webhooks \ -H "Authorization: Bearer $MAINTAINX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.example.com/webhooks/maintainx", "events": [ "workorder.created", "workorder.updated", "workorder.status_changed", "workorder.completed" ] }' Step 2: Webhook Receiver (Express) // src/webhook-server.ts import express from 'express'; import crypto from 'node:crypto'; const app = express(); app.use(express.json({ limit: '1mb' })); // Signature verification middleware function verifySignature(secret: string) { return (req: express.Request, res: express.Response, next: express.NextFunction) => { const signature = req.headers['x-maintainx-signature'] as string; if (!signature) { return res.status(401).json({ error: 'Missing signature header' }); } const expected = crypto .createHmac('sha256', secret) .update(JSON.stringify(req.body)) .digest('hex'); if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) { return res.status(401).json({ error: 'Invalid signature' }); } next(); }; } const WEBHOOK_SECRET = process.env.MAINTAINX_WEBHOOK_SECRET!; app.post( '/webhooks/maintainx', verifySignature(WEBHOOK_SECRET), async (req, res) => { const { event, data, timestamp } = req.body; console.log(`[${timestamp}] Event: ${event}, Resource ID: ${data.id}`); // Idempotency check const eventId = req.headers['x-maintainx-event-id'] as string; if (await isProcessed(eventId)) { return res.status(200).json({ status: 'already_processed' }); } try { await routeEvent(event, data); await markProcessed(eventId); res.status(200).json({ status: 'ok' }); } catch (err) { console.error('Webhook handler error:', err); res.status(500).json({ error: 'Processing failed' }); } }, ); app.listen(3000, () => console.log('Webhook server listening on :3000')); Step 3: Event Router // src/event-handlers.ts type EventHandler = (data: any) => Promise
; const handlers: Record
= { 'workorder.created': async (data) => { console.log(`New work order: #${data.id} "${data.title}"`); // Notify Slack, create ticket, etc. if (data.priority === 'HIGH') { await sendSlackAlert(`High priority WO created: ${data.title}`); } }, 'workorder.status_changed': async (data) => { console.log(`WO #${data.id}: ${data.previousStatus} → ${data.status}`); if (data.status === 'COMPLETED') { await syncCompletionToERP(data); } }, 'workorder.completed': async (data) => { console.log(`WO #${data.id} completed at ${data.completedAt}`); await generateCompletionReport(data); }, 'workorder.updated': async (data) => { await syncWorkOrderToDataWarehouse(data); }, }; export async function routeEvent(event: string, data: any) { const handler = handlers[event]; if (handler) { await handler(data); } else { console.warn(`Unhandled event type: ${event}`); } } Step 4: Idempotency Store // src/idempotency.ts // Use Redis in production; Map for dev/testing const processed = new Map
(); export async function isProcessed(eventId: string): Promise
{ return processed.has(eventId); } export async function markProcessed(eventId: string): Promise
{ processed.set(eventId, true); // In production: await redis.setex(`event:${eventId}`, 86400, '1'); } Step 5: Local Development with ngrok # Start your webhook server npm run dev # In another terminal, expose it with ngrok ngrok http 3000 # Copy the https URL (e.g., https://abc123.ngrok-free.app) # Register the ngrok URL as a webhook curl -X POST https://api.getmaintainx.com/v1/webhooks \ -H "Authorization: Bearer $MAINTAINX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://abc123.ngrok-free.app/webhooks/maintainx", "events": ["workorder.created", "workorder.status_changed"] }' Step 6: List and Manage Webhooks # List all registered webhooks curl -s https://api.getmaintainx.com/v1/webhooks \ -H "Authorization: Bearer $MAINTAINX_API_KEY" | jq . # Delete a webhook (replace ID) curl -X DELETE https://api.getmaintainx.com/v1/webhooks/456 \ -H "Authorization: Bearer $MAINTAINX_API_KEY" Output Webhook endpoint with HMAC signature verification Event router dispatching to type-specific handlers Idempotency guard preventing duplicate processing Local dev setup with ngrok for testing webhooks Webhook registration and management via REST API Error Handling Issue Cause Solution 401 on webhook registration Invalid API key Verify MAINTAINXAPIKEY Webhook not firing URL not reachable Ensure HTTPS, check firewall, test with ngrok Duplicate events Retries from MaintainX Implement idempotency with event ID deduplication Signature mismatch Wrong secret or body mutation Verify raw body is used for HMAC, check secret value Resources MaintainX API Reference ngrok Documentation Webhook Security Best Practices Next Steps For performance optimization, see maintainx-performance-tuning. Examples Slack notification on high-priority work orders: async function sendSlackAlert(message: string) { await fetch(process.env.SLACK_WEBHOOK_URL!, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: `:rotating_light: MaintainX Alert: ${message}`, }), }); } Polling fallback when webhooks are unavailable: // Poll every 60 seconds for status changes async function pollWorkOrders(client: MaintainXClient, since: string) { const { workOrders } = await client.getWorkOrders({ updatedAtGte: since, limit: 50, }); for (const wo of workOrders) { await routeEvent('workorder.updated', wo); } return new Date().toISOString(); }
https://tonsofskills.com/skills/maintainx-webhooks-events/
Reply
Anonymous
Information Epoch 1793287763
Files are bags of bytes.
Home
Notebook
Contact us