maintainx-sdk-patterns
Apply standard patterns for MaintainX API pagination, error handling, and type-safe client design.
Install
mkdir -p .claude/skills/maintainx-sdk-patterns && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/8636" && unzip -o skill.zip -d .claude/skills/maintainx-sdk-patterns && rm skill.zipInstalls to .claude/skills/maintainx-sdk-patterns
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Learn MaintainX REST API patterns, pagination, filtering, and clientKey capabilities
- →Build a type-safe client for the MaintainX API
- →Implement cursor-based pagination for MaintainX list endpoints
- →Apply retry logic with exponential backoff for API calls
- →Manage concurrent batch operations with rate limiting
- →Create fluent query builders for MaintainX work orders
- →Stream large datasets using async iterators
How it works
The skill provides code patterns for a type-safe MaintainX client, cursor-based pagination, and retry logic with exponential backoff. It also includes a rate-limited batch processor and a fluent query builder.
Inputs & outputs
When to use maintainx-sdk-patterns
- →Implement cursor-based pagination
- →Create type-safe API clients
- →Handle API error and retry logic
- →Structure data models for MaintainX entities
About this skill
MaintainX SDK Patterns
Overview
Production-grade patterns for building robust MaintainX API integrations with proper error handling, cursor-based pagination, retry logic, and type safety.
Prerequisites
- Completed
maintainx-install-authsetup - TypeScript/Node.js familiarity
- Understanding of REST API principles
Instructions
Step 1: Type-Safe Client with Generics
// src/maintainx/typed-client.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios';
interface PaginatedResponse<T> {
cursor: string | null;
}
interface WorkOrder {
id: number;
title: string;
status: 'OPEN' | 'IN_PROGRESS' | 'ON_HOLD' | 'COMPLETED' | 'CLOSED';
priority: 'NONE' | 'LOW' | 'MEDIUM' | 'HIGH';
description?: string;
assignees: Array<{ type: 'USER' | 'TEAM'; id: number }>;
assetId?: number;
locationId?: number;
createdAt: string;
updatedAt: string;
completedAt?: string;
dueDate?: string;
categories: string[];
}
interface Asset {
id: number;
name: string;
serialNumber?: string;
model?: string;
manufacturer?: string;
locationId?: number;
createdAt: string;
}
interface WorkOrdersResponse extends PaginatedResponse<WorkOrder> {
workOrders: WorkOrder[];
}
interface AssetsResponse extends PaginatedResponse<Asset> {
assets: Asset[];
}
export class MaintainXClient {
private http: AxiosInstance;
constructor(apiKey?: string) {
const key = apiKey || process.env.MAINTAINX_API_KEY;
if (!key) throw new Error('MAINTAINX_API_KEY required');
this.http = axios.create({
baseURL: 'https://api.getmaintainx.com/v1',
headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
timeout: 30_000,
});
}
async getWorkOrders(params?: Record<string, any>): Promise<WorkOrdersResponse> {
const { data } = await this.http.get<WorkOrdersResponse>('/workorders', { params });
return data;
}
async getWorkOrder(id: number): Promise<WorkOrder> {
const { data } = await this.http.get<WorkOrder>(`/workorders/${id}`);
return data;
}
async createWorkOrder(input: Partial<WorkOrder>): Promise<WorkOrder> {
const { data } = await this.http.post<WorkOrder>('/workorders', input);
return data;
}
async updateWorkOrder(id: number, input: Partial<WorkOrder>): Promise<WorkOrder> {
const { data } = await this.http.patch<WorkOrder>(`/workorders/${id}`, input);
return data;
}
async getAssets(params?: Record<string, any>): Promise<AssetsResponse> {
const { data } = await this.http.get<AssetsResponse>('/assets', { params });
return data;
}
async request<T = any>(method: string, path: string, body?: any): Promise<T> {
const config: AxiosRequestConfig = { method, url: path, data: body };
const { data } = await this.http.request<T>(config);
return data;
}
}
Step 2: Cursor-Based Pagination
MaintainX uses cursor-based pagination. The response includes a cursor field; pass it as a query parameter to get the next page.
async function paginate<T>(
fetcher: (cursor?: string) => Promise<{ cursor: string | null } & Record<string, T[]>>,
key: string,
): Promise<T[]> {
const all: T[] = [];
let cursor: string | undefined;
do {
const response = await fetcher(cursor);
const items = (response as any)[key] as T[];
all.push(...items);
cursor = response.cursor ?? undefined;
} while (cursor);
return all;
}
// Usage
const allWorkOrders = await paginate(
(cursor) => client.getWorkOrders({ limit: 100, cursor, status: 'OPEN' }),
'workOrders',
);
console.log(`Total open work orders: ${allWorkOrders.length}`);
const allAssets = await paginate(
(cursor) => client.getAssets({ limit: 100, cursor }),
'assets',
);
console.log(`Total assets: ${allAssets.length}`);
Step 3: Retry with Exponential Backoff
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3,
baseDelayMs = 1000,
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (err: any) {
const status = err?.response?.status;
const isRetryable = status === 429 || (status >= 500 && status < 600);
if (!isRetryable || attempt === maxRetries) throw err;
// Honor Retry-After header if present
const retryAfter = err.response?.headers?.['retry-after'];
const delayMs = retryAfter
? parseInt(retryAfter) * 1000
: baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
console.warn(`Retry ${attempt + 1}/${maxRetries} after ${delayMs}ms (HTTP ${status})`);
await new Promise((r) => setTimeout(r, delayMs));
}
}
throw new Error('Unreachable');
}
// Usage
const wo = await withRetry(() => client.getWorkOrder(12345));
Step 4: Batch Operations
import PQueue from 'p-queue';
const queue = new PQueue({ concurrency: 5, interval: 1000, intervalCap: 10 });
async function batchCreateWorkOrders(items: Array<Partial<WorkOrder>>): Promise<WorkOrder[]> {
const results = await Promise.all(
items.map((item) =>
queue.add(() => withRetry(() => client.createWorkOrder(item)))
),
);
return results as WorkOrder[];
}
// Create 50 PMs in controlled batches
const pms = Array.from({ length: 50 }, (_, i) => ({
title: `Weekly Inspection - Zone ${i + 1}`,
priority: 'LOW' as const,
categories: ['PREVENTIVE'],
}));
const created = await batchCreateWorkOrders(pms);
console.log(`Created ${created.length} preventive maintenance orders`);
Step 5: Fluent Query Builder
class WorkOrderQuery {
private params: Record<string, any> = {};
status(s: WorkOrder['status']) { this.params.status = s; return this; }
priority(p: WorkOrder['priority']) { this.params.priority = p; return this; }
assignee(userId: number) { this.params.assigneeId = userId; return this; }
asset(assetId: number) { this.params.assetId = assetId; return this; }
location(locationId: number) { this.params.locationId = locationId; return this; }
createdAfter(date: string) { this.params.createdAtGte = date; return this; }
createdBefore(date: string) { this.params.createdAtLte = date; return this; }
limit(n: number) { this.params.limit = n; return this; }
async execute(client: MaintainXClient) {
return client.getWorkOrders(this.params);
}
}
// Usage
const results = await new WorkOrderQuery()
.status('OPEN')
.priority('HIGH')
.location(2345)
.createdAfter('2026-01-01T00:00:00Z')
.limit(25)
.execute(client);
Output
- Type-safe MaintainX client with full TypeScript interfaces
- Cursor-based pagination utility that works across all list endpoints
- Retry logic with exponential backoff and
Retry-Afterheader support - Rate-limited batch processor using
p-queue - Fluent query builder for readable work order filters
Error Handling
| Pattern | Use Case |
|---|---|
withRetry() | Transient errors (429, 5xx) with exponential backoff |
paginate() | Collecting all items from cursor-based endpoints |
PQueue | Controlled concurrency to avoid rate limits |
WorkOrderQuery | Type-safe filtering to prevent invalid API calls |
Resources
- MaintainX API Reference
- p-queue -- Promise queue with concurrency control
Next Steps
For core workflows, see maintainx-core-workflow-a (Work Orders) and maintainx-core-workflow-b (Assets).
Examples
Stream large datasets with async iterators:
async function* streamWorkOrders(client: MaintainXClient, params?: Record<string, any>) {
let cursor: string | undefined;
do {
const response = await client.getWorkOrders({ ...params, limit: 100, cursor });
for (const wo of response.workOrders) {
yield wo;
}
cursor = response.cursor ?? undefined;
} while (cursor);
}
for await (const wo of streamWorkOrders(client, { status: 'COMPLETED' })) {
console.log(`Processing completed WO #${wo.id}`);
}
When not to use it
- →When not interacting with the MaintainX REST API
- →When not requiring cursor-based pagination for list endpoints
- →When not needing retry logic for transient API errors
Prerequisites
Limitations
- →The patterns are specific to the MaintainX REST API
- →Batch operations are rate-limited by the `p-queue` configuration
- →Type-safe filtering is provided only for work order queries
How it compares
This skill provides production-grade patterns for MaintainX API integrations, offering type safety, pagination, and error handling that go beyond basic API requests.
Compared to similar skills
maintainx-sdk-patterns side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| maintainx-sdk-patterns (this skill) | 0 | 27d | Caution | Intermediate |
| mcp-builder | 136 | 3mo | Review | Advanced |
| telegram-bot-builder | 106 | 6mo | Review | Intermediate |
| telegram-mini-app | 62 | 6mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
mcp-builder
anthropics
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
telegram-bot-builder
davila7
Expert in building Telegram bots that solve real problems - from simple automation to complex AI-powered bots. Covers bot architecture, the Telegram Bot API, user experience, monetization strategies, and scaling bots to thousands of users. Use when: telegram bot, bot api, telegram automation, chat bot telegram, tg bot.
telegram-mini-app
davila7
Expert in building Telegram Mini Apps (TWA) - web apps that run inside Telegram with native-like experience. Covers the TON ecosystem, Telegram Web App API, payments, user authentication, and building viral mini apps that monetize. Use when: telegram mini app, TWA, telegram web app, TON app, mini app.
stripe-integration
wshobson
Implement Stripe payment processing for robust, PCI-compliant payment flows including checkout, subscriptions, and webhooks. Use when integrating Stripe payments, building subscription systems, or implementing secure checkout flows.
n8n-expression-syntax
czlonkowski
Validate n8n expression syntax and fix common errors. Use when writing n8n expressions, using {{}} syntax, accessing $json/$node variables, troubleshooting expression errors, or working with webhook data in workflows.
langchain-architecture
wshobson
Design LLM applications using the LangChain framework with agents, memory, and tool integration patterns. Use when building LangChain applications, implementing AI agents, or creating complex LLM workflows.