Skip to content
Back to skills

Appfolio Core Workflow A

ASecurity

'Build property management dashboard with AppFolio API data.

  • 2,719 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 21, 2026
ai-agentstypescriptbashapisecurity

Works with

  • claude code
  • cursor
  • cli
  • api

Security analysis

A100/100

Scanned September 21, 2026

npx -y skills add jeremylongshore/claude-code-plugins-plus --skill appfolio-core-workflow-a --agent claude-code

Installs 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 with every re-scan.

Security grade badge for Appfolio Core Workflow A
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jeremylongshore-appfolio-core-workflow-a-794af74f/badge)](https://www.skillsdirectory.com/skills/jeremylongshore-appfolio-core-workflow-a-794af74f)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.