'Build property management dashboard with AppFolio API data.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add jeremylongshore/tons-of-skills-marketplace --skill appfolio-core-workflow-a --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Appfolio Core Workflow A?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-appfolio-core-workflow-a-tons-of-skills-marketplace)More formats (shields.io, HTML) on the badges page.
---
name: appfolio-core-workflow-a
description: 'Build property management dashboard with AppFolio API data.
Trigger: "appfolio property dashboard".
'
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(curl:*), Grep
version: 1.5.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- property-management
- appfolio
- real-estate
compatibility: Designed for Claude Code
---
# AppFolio — Property & Tenant Management
## Overview
Primary workflow for AppFolio property management integration. Covers the full property
lifecycle: creating and updating property records, managing tenant profiles and lease
agreements, and querying occupancy data. Uses the AppFolio Stack API with OAuth 2.0
client credentials for server-to-server access. All endpoints return JSON and support
pagination via `cursor` parameters for large portfolios.
## Prerequisites
- A current AppFolio partner/API contract that confirms the assigned portfolio
base URL, authentication method, approved scopes, and write capabilities;
do not infer these from a tutorial or another portfolio.
- A separate sandbox with disposable properties, units, and synthetic tenant
records, plus a credential owner and an approved request budget.
- Idempotency keys and an operator-approved rollback/reconciliation process for
property, tenant, and lease writes, which can create real legal and billing
consequences.
## Instructions
1. Confirm the provider-issued base URL and authentication flow for the target
portfolio before copying any client configuration into an environment.
2. Exercise the create/update path only with sandbox synthetic fixtures and
idempotency keys; retrieve canonical records before retrying an uncertain
write.
3. Validate ownership, lease dates, and unit availability before creating a
lease, and emit only record IDs and redacted status evidence.
4. Promote a write-capable flow only after a staged rehearsal proves rollback,
reconciliation, and the least-privilege scope boundary.
### Step 1: Authenticate and Initialize Client
```typescript
const token = await fetch('https://api.appfolio.com/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.APPFOLIO_CLIENT_ID!,
client_secret: process.env.APPFOLIO_CLIENT_SECRET!,
scope: 'properties tenants leases',
}),
}).then(r => r.json());
const headers = { Authorization: `Bearer ${token.access_token}`, 'Content-Type': 'application/json' };
```
### Step 2: Create or Update a Property
```typescript
const property = await fetch('https://api.appfolio.com/v1/properties', {
method: 'POST', headers,
body: JSON.stringify({
name: 'Sunrise Apartments',
address: { street: '100 Main St', city: 'Austin', state: 'TX', zip: '78701' },
type: 'residential',
units: [
{ number: '101', bedrooms: 2, bathrooms: 1, rent: 1450 },
{ number: '102', bedrooms: 1, bathrooms: 1, rent: 1100 },
],
}),
}).then(r => r.json());
console.log(`Property created: ${property.id}`);
```
### Step 3: Add a Tenant and Lease
```typescript
const tenant = await fetch('https://api.appfolio.com/v1/tenants', {
method: 'POST', headers,
body: JSON.stringify({
first_name: 'Test', last_name: 'Tenant',
email: 'test-tenant@example.invalid', phone: '555-0100',
}),
}).then(r => r.json());
await fetch('https://api.appfolio.com/v1/leases', {
method: 'POST', headers,
body: JSON.stringify({
property_id: property.id, unit_number: '101',
tenant_id: tenant.id, start_date: '2026-05-01', end_date: '2027-04-30',
monthly_rent: 1450, security_deposit: 1450,
}),
}).then(r => r.json());
```
### Step 4: Query Occupancy
```typescript
const units = await fetch(
`https://api.appfolio.com/v1/properties/${property.id}/units?status=vacant`,
{ headers },
).then(r => r.json());
console.log(`Vacant units: ${units.data.length} of ${units.meta.total}`);
```
## Error Handling
| Issue | Cause | Fix |
|-------|-------|-----|
| `401 Unauthorized` | Expired or invalid token | Re-authenticate with client credentials |
| `404 Not Found` | Wrong property/tenant ID | Verify resource ID exists before referencing |
| `422 Unprocessable` | Missing required fields | Check `errors[]` array in response body |
| `409 Conflict` | Duplicate lease for unit | Query existing leases before creating |
| `429 Too Many Requests` | Rate limit exceeded | Back off using `Retry-After` header value |
## Output
A successful run creates a property with units, adds a tenant, binds them via a lease,
and reports vacancy counts. Console output confirms each resource ID on creation.
## Examples
For a lease-creation change, run against a named synthetic sandbox unit and
tenant with a stable idempotency key. Read the property and current leases,
perform the candidate write once, then read back the resulting lease and record
only IDs, dates, and the reconciliation status. Re-run the same request to
prove it does not create a second lease. If the provider contract, endpoint,
authorization, or prior-write outcome cannot be verified, stop before any
write and send the case to the portfolio operator for reconciliation.
## Resources
- [AppFolio Stack APIs](https://www.appfolio.com/stack/partners/api)
- [AppFolio Engineering Blog](https://engineering.appfolio.com)
## Next Steps
Continue with `appfolio-core-workflow-b` for maintenance requests and payment tracking.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!