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".
| 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.
Other measured skills in the registry, with their headline benchmark lift.