From appfolio-pack
Migrate between AppFolio API versions and handle endpoint changes. Trigger: "appfolio upgrade".
npx claudepluginhub flight505/skill-forge --plugin appfolio-packThis skill is limited to using the following tools:
AppFolio property management integrations depend on versioned REST API endpoints that
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
AppFolio property management integrations depend on versioned REST API endpoints that evolve with the platform. Upgrades can rename fields on property and tenant objects, change pagination models, deprecate work-order endpoints, and alter the basic-auth flow. This skill detects your current API version, maps deprecated response shapes to replacements, and rolls back automatically if the new version fails.
/api/v1/)After a successful migration the skill produces:
VersionInfo object confirming current, latest, and deprecated versionsinterface VersionInfo { current: string; latest: string; deprecated: string[]; }
async function detectApiVersion(baseUrl: string, headers: Record<string, string>): Promise<VersionInfo> {
const res = await fetch(`${baseUrl}/api/status`, { headers });
const body = await res.json();
const current = res.headers.get("X-AppFolio-Api-Version") ?? body.api_version;
const deprecated: string[] = body.deprecated_versions ?? [];
if (deprecated.includes(current)) {
console.warn(`Version ${current} is deprecated. Migrate to ${body.latest_version}.`);
}
return { current, latest: body.latest_version, deprecated };
}
interface LegacyProperty { address_line1: string; unit_count: number; mgr_id: string; }
interface CurrentProperty { street_address: string; total_units: number; manager_id: string; }
function migrateProperty(old: LegacyProperty): CurrentProperty {
return { street_address: old.address_line1, total_units: old.unit_count, manager_id: old.mgr_id };
}
interface LegacyTenant { lease_end: string; balance_due: number; }
interface CurrentTenant { lease_expiry_date: string; outstanding_balance: number; }
function migrateTenant(old: LegacyTenant): CurrentTenant {
return { lease_expiry_date: old.lease_end, outstanding_balance: old.balance_due };
}
async function versionAwareRequest(
baseUrl: string, path: string, headers: Record<string, string>,
targetVersion: string, fallbackVersion: string
): Promise<any> {
const res = await fetch(`${baseUrl}/api/${targetVersion}${path}`, { headers });
if (res.status === 410 || res.status === 404) {
console.warn(`${targetVersion} rejected; falling back to ${fallbackVersion}`);
const fallback = await fetch(`${baseUrl}/api/${fallbackVersion}${path}`, { headers });
if (!fallback.ok) throw new Error(`Fallback failed: ${fallback.status}`);
return fallback.json();
}
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
return res.json();
}
// Detect version and migrate if needed
const info = await detectApiVersion("https://acme.appfolio.com", authHeaders);
if (info.deprecated.includes(info.current)) {
const oldProps = await fetchLegacyProperties();
const migrated = oldProps.map(migrateProperty);
await smokeTestEndpoints("https://acme.appfolio.com", authHeaders);
}
| Migration Issue | Symptom | Fix |
|---|---|---|
| Deprecated version prefix | 410 Gone on every request | Update base URL to latest version prefix |
| Renamed property fields | undefined values in property sync | Apply migrateProperty transform |
| Removed pagination offset | Empty result sets after page one | Switch to cursor-based pagination |
| Auth header rejected | 401 Unauthorized after upgrade | Regenerate client secret, update env vars |
| Webhook envelope change | Event handler parse errors | Update payload parser for new envelope |
appfolio-ci-integration for post-migration CI validation