Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build property management dashboard with AppFolio API data. Trigger: "appfolio property dashboard".
.claude/skills/jeremylongshore-appfolio-core-workflow-a/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -14% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -11% | 0% |
| case-05 | ✗→✓ | ▲ Improved | -21% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -13% | 0% |
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.
typescriptconst 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' };
typescriptconst 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}`);
typescriptconst tenant = await fetch('https://api.appfolio.com/v1/tenants', { method: 'POST', headers, body: JSON.stringify({ first_name: 'Alex', last_name: 'Rivera', email: 'alex.rivera@example.com', phone: '512-555-0199', }), }).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());
typescriptconst 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}`);
| 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 |
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.
Continue with appfolio-core-workflow-b for maintenance requests and payment tracking.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 13,816 | 12,670 | -8% | 1 | 1 | 0% | 3,289 | 4,198 | +28% | 0 | 0 | — |
case-02 | fail→pass | 14,887 | 7,892 | -47% | 1 | 1 | 0% | 3,527 | 3,018 | -14% | 0 | 0 | — |
case-03 | fail→pass | 18,635 | 11,464 | -38% | 1 | 1 | 0% | 4,249 | 3,785 | -11% | 0 | 0 | — |
case-04 | pass→pass | 9,842 | 5,487 | -44% | 1 | 1 | 0% | 1,985 | 2,201 | +11% | 0 | 0 | — |
case-05 | fail→pass | 10,230 | 1,920 | -81% | 1 | 1 | 0% | 1,692 | 1,345 | -21% | 0 | 0 | — |
case-11 | fail→pass | 9,474 | 3,371 | -64% | 1 | 1 | 0% | 1,922 | 1,667 | -13% | 0 | 0 | — |
case-06 | fail→pass | 7,122 | 4,244 | -40% | 1 | 1 | 0% | 1,238 | 1,878 | +52% | 0 | 0 | — |
case-07 | fail→pass | 8,258 | 2,972 | -64% | 1 | 1 | 0% | 1,583 | 1,624 | +3% | 0 | 0 | — |
case-08 | fail→pass | 11,303 | 2,043 | -82% | 1 | 1 | 0% | 2,158 | 1,462 | -32% | 0 | 0 | — |
case-09 | fail→pass | 8,731 | 3,448 | -61% | 1 | 1 | 0% | 1,688 | 1,660 | -2% | 0 | 0 | — |
case-10 | fail→pass | 8,203 | 3,611 | -56% | 1 | 1 | 0% | 1,641 | 1,510 | -8% | 0 | 0 | — |
case-12 | pass→pass | 6,313 | 1,675 | -73% | 1 | 1 | 0% | 1,050 | 1,218 | +16% | 0 | 0 | — |
case-13 | pass→pass | 10,369 | 3,051 | -71% | 1 | 1 | 0% | 1,630 | 1,540 | -6% | 0 | 0 | — |
case-14 | pass→pass | 3,233 | 1,921 | -41% | 1 | 1 | 0% | 532 | 1,243 | +134% | 0 | 0 | — |
case-15 | pass→pass | 9,365 | 1,933 | -79% | 1 | 1 | 0% | 1,623 | 1,315 | -19% | 0 | 0 | — |
case-20 | fail→fail | 13,602 | 7,755 | -43% | 1 | 1 | 0% | 2,594 | 2,528 | -3% | 0 | 0 | — |
case-16 | pass→pass | 14,980 | 9,160 | -39% | 1 | 1 | 0% | 2,493 | 2,818 | +13% | 0 | 0 | — |
case-17 | pass→pass | 7,231 | 1,563 | -78% | 1 | 1 | 0% | 1,208 | 1,219 | +1% | 0 | 0 | — |
case-18 | pass→pass | 8,436 | 2,954 | -65% | 1 | 1 | 0% | 1,529 | 1,640 | +7% | 0 | 0 | — |
case-19 | fail→pass | 6,431 | 2,238 | -65% | 1 | 1 | 0% | 1,208 | 1,418 | +17% | 0 | 0 | — |
case-21 | fail→fail | 13,742 | 12,058 | -12% | 1 | 1 | 0% | 2,575 | 3,566 | +38% | 0 | 0 | — |
case-22 | fail→fail | 14,299 | 8,722 | -39% | 1 | 1 | 0% | 2,524 | 2,861 | +13% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +50 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.