openrouter-compliance-review
Review OpenRouter integration for regulatory compliance (SOC2, GDPR,
maintainx-webhooks-events
Implement MaintainX webhook handling and event-driven integrations.
Full skill instructions
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.
MAINTAINX_API_KEY environment variable configuredcurl -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"
]
}'
// 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'));
// src/event-handlers.ts
type EventHandler = (data: any) => Promise<void>;
const handlers: Record<string, EventHandler> = {
'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}`);
}
}
// src/idempotency.ts
// Use Redis in production; Map for dev/testing
const processed = new Map<string, boolean>();
export async function isProcessed(eventId: string): Promise<boolean> {
return processed.has(eventId);
}
export async function markProcessed(eventId: string): Promise<void> {
processed.set(eventId, true);
// In production: await redis.setex(`event:${eventId}`, 86400, '1');
}
# 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"]
}'
# 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"
| Issue | Cause | Solution |
|---|---|---|
| 401 on webhook registration | Invalid API key | Verify MAINTAINX_API_KEY |
| 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 |
For performance optimization, see maintainx-performance-tuning.
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();
}
Review OpenRouter integration for regulatory compliance (SOC2, GDPR,
Data synchronization, ETL patterns, and data management for MaintainX.
Execute Groq primary workflow: chat completions with tool use and JSON
Validate CSRF protection implementations for security gaps. Use when
Configure this skill should be used when the user asks about "API contract",
Execute Ideogram incident response with triage, mitigation, and postmortem.
Execute create, select, and transform features to improve machine learning
Reusable patterns for the Gamma REST API (no SDK exists).
Apply production-ready Deepgram SDK patterns for TypeScript and Python.
Migrate to OpenRouter from direct provider APIs or upgrade between SDK/model
Diagnose and fix common OpenRouter API errors. Use when encountering
Optimize Linear API usage, reduce unnecessary calls, and maximize
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience