appfolio-core-workflow-a

v2026.09.24

Build property management dashboard with AppFolio API data. Trigger: "appfolio property dashboard".

GitHub
Install command
npx skhub add jeremylongshore/appfolio-core-workflow-a
Markdown
SKILL.md

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

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

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

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

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

IssueCauseFix
401 UnauthorizedExpired or invalid tokenRe-authenticate with client credentials
404 Not FoundWrong property/tenant IDVerify resource ID exists before referencing
422 UnprocessableMissing required fieldsCheck errors[] array in response body
409 ConflictDuplicate lease for unitQuery existing leases before creating
429 Too Many RequestsRate limit exceededBack 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

Next Steps

Continue with appfolio-core-workflow-b for maintenance requests and payment tracking.

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/.curated/appfolio-core-workflow-a

Default branch

main

Latest commit

e5a6c3b

Tree SHA

c2dc8e8