Quick reference for diagnosing and resolving common MaintainX API errors with concrete solutions and diagnostic commands.
MAINTAINX_API_KEY environment variable configuredcurl and jq availableRun this first to validate connectivity and auth:
# Test 1: Verify API key is valid
curl -s -o /dev/null -w "%{http_code}" \
https://api.getmaintainx.com/v1/users?limit=1 \
-H "Authorization: Bearer $MAINTAINX_API_KEY"
# Expected: 200
# Test 2: Check API key is set
echo "Key length: ${#MAINTAINX_API_KEY}"
# Should be > 0
# Test 3: Verify DNS resolution
nslookup api.getmaintainx.com
# Should resolve to an IP
Cause: Invalid request body, missing required fields, or malformed JSON.
# Diagnose: Send a minimal valid request
curl -X POST https://api.getmaintainx.com/v1/workorders \
-H "Authorization: Bearer $MAINTAINX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Diagnostic test order"}' -v 2>&1 | tail -5
Common fixes:
title fieldNONE, LOW, MEDIUM, HIGH
OPEN, IN_PROGRESS, ON_HOLD, COMPLETED, CLOSED
2026-03-19T12:00:00Z
Cause: Missing, invalid, or expired API key.
// Diagnostic wrapper
async function diagAuth() {
try {
const res = await fetch('https://api.getmaintainx.com/v1/users?limit=1', {
headers: { Authorization: `Bearer ${process.env.MAINTAINX_API_KEY}` },
});
if (res.status === 401) {
console.error('API key is invalid or expired.');
console.error('Generate a new key: MaintainX > Settings > Integrations > New Key');
} else {
console.log('Auth OK - status:', res.status);
}
} catch (e) {
console.error('Network error:', e);
}
}
Fixes:
echo "'$MAINTAINX_API_KEY'" | cat -A
Bearer prefix (not Basic or Token)Cause: Valid key but insufficient permissions or wrong plan tier.
Fixes:
X-Organization-Id headerCause: Resource ID does not exist or wrong endpoint path.
# Verify a resource exists before referencing it
curl -s "https://api.getmaintainx.com/v1/workorders/99999" \
-H "Authorization: Bearer $MAINTAINX_API_KEY" | jq '.message // .title'
Fixes:
api.getmaintainx.com/v1 (not /v2 or missing version)/workorders not /work-orders)Cause: Request is syntactically valid but semantically incorrect.
Common triggers:
CLOSED to IN_PROGRESS)assetId or locationId
Cause: Rate limit exceeded.
// Auto-retry on 429
async function safeRequest(fn: () => Promise<any>, retries = 3) {
for (let i = 0; i <= retries; i++) {
try {
return await fn();
} catch (err: any) {
if (err?.response?.status === 429 && i < retries) {
const wait = parseInt(err.response.headers['retry-after'] || '5') * 1000;
console.warn(`Rate limited. Waiting ${wait}ms...`);
await new Promise(r => setTimeout(r, wait));
} else {
throw err;
}
}
}
}
Fixes:
Retry-After response headermaintainx-rate-limits)Cause: MaintainX server-side issue.
Fixes:
For comprehensive debugging, see maintainx-debug-bundle.
Full diagnostic script:
#!/bin/bash
echo "=== MaintainX API Diagnostics ==="
echo "Key set: $([ -n "$MAINTAINX_API_KEY" ] && echo 'YES' || echo 'NO')"
echo "Key length: ${#MAINTAINX_API_KEY}"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
https://api.getmaintainx.com/v1/users?limit=1 \
-H "Authorization: Bearer $MAINTAINX_API_KEY")
echo "Auth status: $STATUS"
if [ "$STATUS" = "200" ]; then
WO_COUNT=$(curl -s "https://api.getmaintainx.com/v1/workorders?limit=1" \
-H "Authorization: Bearer $MAINTAINX_API_KEY" | jq '.workOrders | length')
echo "Work orders accessible: $WO_COUNT"
fi