Skills Development AppFolio API Debug Collection Bundle

AppFolio API Debug Collection Bundle

v20260423
appfolio-debug-bundle
A comprehensive debug bundle designed to collect diagnostic evidence from AppFolio property management API integrations. It captures essential data points including API connectivity status for properties, tenants, and work orders; authentication status using client credentials; recent integration error logs; and environment metadata. This allows support engineers to perform root cause analysis and diagnose issues like connectivity failures or authentication rejections without needing live environment access.
Get Skill
232 downloads
Overview

AppFolio Debug Bundle

Overview

This debug bundle collects diagnostic evidence from AppFolio property management API integrations
for support escalation and root cause analysis. It captures API connectivity against the
properties, tenants, and work orders endpoints, authentication status using client credential
pairs, recent error logs from integration pipelines, and SDK version information. The resulting
tarball gives support engineers everything they need to diagnose connectivity failures, auth
rejections, and data sync issues without requiring live access to your environment.

Prerequisites

  • curl, jq, tar installed
  • APPFOLIO_CLIENT_ID and APPFOLIO_CLIENT_SECRET configured (basic auth pair)
  • APPFOLIO_BASE_URL set to your Stack API base (e.g., https://yourcompany.appfolio.com/api/v1)

Debug Collection Script

#!/bin/bash
set -euo pipefail
BUNDLE="debug-appfolio-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BUNDLE"

# Environment check
echo "=== Environment ===" > "$BUNDLE/environment.txt"
echo "Base URL: ${APPFOLIO_BASE_URL:-NOT SET}" >> "$BUNDLE/environment.txt"
echo "Client ID: ${APPFOLIO_CLIENT_ID:+SET (redacted)}" >> "$BUNDLE/environment.txt"
echo "Client Secret: ${APPFOLIO_CLIENT_SECRET:+SET (redacted)}" >> "$BUNDLE/environment.txt"
echo "Node: $(node -v 2>/dev/null || echo 'not installed')" >> "$BUNDLE/environment.txt"
echo "Timestamp: $(date -u)" >> "$BUNDLE/environment.txt"

# API connectivity — properties endpoint
echo "=== API Health ===" > "$BUNDLE/api-health.txt"
curl -sf -o "$BUNDLE/api-health.txt" -w "HTTP %{http_code} in %{time_total}s\n" \
  -u "${APPFOLIO_CLIENT_ID}:${APPFOLIO_CLIENT_SECRET}" \
  "${APPFOLIO_BASE_URL}/properties?per_page=1" 2>&1 || echo "UNREACHABLE" > "$BUNDLE/api-health.txt"

# Work orders endpoint probe
echo "=== Work Orders ===" > "$BUNDLE/work-orders.txt"
curl -sf -w "HTTP %{http_code}\n" \
  -u "${APPFOLIO_CLIENT_ID}:${APPFOLIO_CLIENT_SECRET}" \
  "${APPFOLIO_BASE_URL}/work_orders?per_page=1" >> "$BUNDLE/work-orders.txt" 2>&1 || echo "FAILED" >> "$BUNDLE/work-orders.txt"

# Tenant endpoint probe
echo "=== Tenants ===" > "$BUNDLE/tenants.txt"
curl -sf -w "HTTP %{http_code}\n" \
  -u "${APPFOLIO_CLIENT_ID}:${APPFOLIO_CLIENT_SECRET}" \
  "${APPFOLIO_BASE_URL}/tenants?per_page=1" >> "$BUNDLE/tenants.txt" 2>&1 || echo "FAILED" >> "$BUNDLE/tenants.txt"

# Recent integration logs
echo "=== Recent Logs ===" > "$BUNDLE/app-logs.txt"
tail -100 /var/log/appfolio-sync/*.log >> "$BUNDLE/app-logs.txt" 2>/dev/null || echo "No sync logs found" >> "$BUNDLE/app-logs.txt"

# Rate limit headers
echo "=== Rate Limits ===" > "$BUNDLE/rate-limits.txt"
curl -sI -u "${APPFOLIO_CLIENT_ID}:${APPFOLIO_CLIENT_SECRET}" \
  "${APPFOLIO_BASE_URL}/properties?per_page=1" 2>/dev/null | grep -i "x-rate\|retry-after\|x-ratelimit" >> "$BUNDLE/rate-limits.txt" || echo "No rate limit headers" >> "$BUNDLE/rate-limits.txt"

# Package versions
echo "=== Dependencies ===" > "$BUNDLE/deps.txt"
npm ls 2>/dev/null | grep -i appfolio >> "$BUNDLE/deps.txt" || echo "No AppFolio npm packages found" >> "$BUNDLE/deps.txt"

tar -czf "$BUNDLE.tar.gz" "$BUNDLE" && rm -rf "$BUNDLE"
echo "Bundle: $BUNDLE.tar.gz"

Analyzing the Bundle

tar -xzf debug-appfolio-*.tar.gz
cat debug-appfolio-*/environment.txt     # Verify credentials are set
cat debug-appfolio-*/api-health.txt      # Check HTTP status and latency
cat debug-appfolio-*/rate-limits.txt     # Confirm not throttled
jq '.errors' debug-appfolio-*/work-orders.txt 2>/dev/null  # Parse error payloads

Common Issues

SymptomCheck in BundleFix
401 on all endpointsenvironment.txt shows client ID/secret NOT SETSet APPFOLIO_CLIENT_ID and APPFOLIO_CLIENT_SECRET in env
403 Forbidden on tenantstenants.txt HTTP 403Stack API scope missing; request tenant read permission in AppFolio partner portal
429 Too Many Requestsrate-limits.txt shows retry-after headerBack off and implement exponential retry; AppFolio allows 120 req/min
Timeout on work ordersapi-health.txt shows time > 30sReduce per_page parameter; filter by updated_since to narrow result set
Empty property listapi-health.txt returns []Verify APPFOLIO_BASE_URL points to correct portfolio; check property group filters
SSL certificate errorapi-health.txt shows curl SSL errorUpdate CA bundle: sudo update-ca-certificates; check proxy settings

Automated Health Check

async function checkAppFolioHealth(): Promise<{
  status: string;
  latencyMs: number;
  endpoints: Record<string, number>;
}> {
  const baseUrl = process.env.APPFOLIO_BASE_URL;
  const creds = Buffer.from(
    `${process.env.APPFOLIO_CLIENT_ID}:${process.env.APPFOLIO_CLIENT_SECRET}`
  ).toString("base64");
  const headers = { Authorization: `Basic ${creds}` };
  const endpoints = ["properties", "tenants", "work_orders"];
  const results: Record<string, number> = {};
  const start = Date.now();
  for (const ep of endpoints) {
    const res = await fetch(`${baseUrl}/${ep}?per_page=1`, { headers });
    results[ep] = res.status;
  }
  return {
    status: Object.values(results).every((s) => s === 200) ? "healthy" : "degraded",
    latencyMs: Date.now() - start,
    endpoints: results,
  };
}

Resources

Next Steps

See appfolio-rate-limits.

Info
Category Development
Name appfolio-debug-bundle
Version v20260423
Size 5.8KB
Updated At 2026-04-28
Language