Communitygithub.com

jeremylongshore/tons-of-skills-marketplace

Apply production-ready patterns for AppFolio REST API integration. Trigger: "appfolio patterns".

Qu'est-ce que tons-of-skills-marketplace ?

tons-of-skills-marketplace is a Claude Code agent skill that apply production-ready patterns for AppFolio REST API integration. Trigger: "appfolio patterns".

Compatible avec✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/jeremylongshore/tons-of-skills-marketplace/tree/HEAD/plugins/saas-packs/appfolio-pack/skills/appfolio-sdk-patterns

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

AppFolio SDK Patterns

Overview

Production-ready patterns for the AppFolio property management REST API. AppFolio uses HTTP Basic Auth with client credentials and returns JSON responses for properties, tenants, leases, and work orders. A structured singleton client prevents credential sprawl, enforces consistent error handling, and centralizes pagination logic across all property management endpoints.

Prerequisites

  • A provider-verified base URL, auth method, endpoint scope, and managed secret injection path for the target portfolio.
  • Schema validation, request timeouts, endpoint-specific rate limits, and idempotency keys for all mutation-capable service methods.
  • Synthetic fixtures for unit testing; production tenant, lease, and payment payloads must not become default mock, log, or error-message content.

Instructions

  1. Construct one contract-bound client per runtime and reject missing or invalid configuration before requests begin.
  2. Validate query bounds and response shapes at the service boundary, then pass only minimized typed fields to callers.
  3. Return a classified 429 or unknown-write failure to the caller; retry only idempotent operations after the caller records the cursor/key and delay.
  4. Keep write workflows behind explicit authorization, audit, and reconciliation controls rather than embedding them in generic SDK convenience helpers.

Singleton Client

import axios, { AxiosInstance } from 'axios';
let _client: AxiosInstance | null = null;
export function getClient(): AxiosInstance {
  if (!_client) {
    const clientId = process.env.APPFOLIO_CLIENT_ID;
    const clientSecret = process.env.APPFOLIO_CLIENT_SECRET;
    const baseURL = process.env.APPFOLIO_BASE_URL;
    if (!clientId || !clientSecret || !baseURL) throw new Error('APPFOLIO_CLIENT_ID, SECRET, and BASE_URL required');
    _client = axios.create({ baseURL, auth: { username: clientId, password: clientSecret }, timeout: 30000 });
  }
  return _client;
}

Error Wrapper

export class AppFolioError extends Error {
  constructor(public status: number, public code: string, message: string) { super(message); }
}
export async function safeCall<T>(operation: string, fn: () => Promise<T>): Promise<T> {
  try { return await fn(); }
  catch (err: any) {
    const status = err.response?.status ?? 0;
    if (status === 429) {
      const retryAfter = err.response?.headers?.['retry-after'];
      throw new AppFolioError(429, 'RATE_LIMIT', `Retry after ${retryAfter ?? 'provider-directed delay'}; do not replay an unknown write automatically`);
    }
    if (status === 401) throw new AppFolioError(401, 'AUTH', 'Invalid APPFOLIO_CLIENT_ID or SECRET');
    throw new AppFolioError(status, 'API_ERROR', `${operation} failed [${status}]: ${err.message}`);
  }
}

Request Builder

class AppFolioQuery {
  private params: Record<string, string> = {};
  status(s: 'active' | 'past' | 'future') { this.params.status = s; return this; }
  propertyId(id: string) { this.params.property_id = id; return this; }
  page(n: number) { this.params.page = String(n); return this; }
  perPage(n: number) { this.params.per_page = String(Math.min(n, 200)); return this; }
  since(date: string) { this.params.updated_since = date; return this; }
  build() { return this.params; }
}
// Usage: new AppFolioQuery().status('active').perPage(50).build();

Response Types

interface Property {
  id: string; name: string; property_type: 'residential' | 'commercial' | 'mixed';
  address: { street: string; city: string; state: string; zip: string };
  unit_count: number; status: string;
}
interface Tenant {
  id: string; first_name: string; last_name: string;
  email: string; phone: string; unit_id: string; lease_id: string;
}
interface Lease {
  id: string; unit_id: string; tenant_id: string;
  start_date: string; end_date: string; rent_amount: number; status: 'active' | 'expired' | 'future';
}
interface WorkOrder {
  id: string; property_id: string; unit_id: string;
  description: string; priority: 'low' | 'medium' | 'high' | 'emergency';
  status: 'open' | 'in_progress' | 'completed';
}

Testing Utilities

export function mockProperty(overrides: Partial<Property> = {}): Property {
  return { id: 'prop-001', name: 'Maple Ridge Apts', property_type: 'residential',
    address: { street: '100 Main St', city: 'Austin', state: 'TX', zip: '78701' },
    unit_count: 24, status: 'active', ...overrides };
}
export function mockLease(overrides: Partial<Lease> = {}): Lease {
  return { id: 'lease-001', unit_id: 'unit-001', tenant_id: 'ten-001',
    start_date: '2025-01-01', end_date: '2026-01-01', rent_amount: 1500, status: 'active', ...overrides };
}

Error Handling

PatternWhen to UseExample
safeCall wrapperAll API callsPrevents uncaught 4xx/5xx from crashing flows
Caller-owned retry on 429Rate-limited idempotent batch readsPreserve cursor/key, honor Retry-After, then retry deliberately
Auth validationClient initThrows early if credentials are missing
Pagination loopListing properties/tenantsIncrement page until empty response

Output

  • One validated, contract-bound API client with centralized timeout and error classification behavior
  • Bounded query parameters and minimized typed responses for service callers
  • Explicit rate-limit and unknown-write outcomes that require caller-owned cursor/idempotency/reconciliation decisions

Examples

For a property-list read, build a query with a capped page size, execute it through safeCall, and verify the returned property IDs and count against a synthetic fixture. For a mutation, persist an idempotency key before dispatch and treat a timeout or 429 as a pause/reconcile condition rather than calling the function again. If client configuration, response schema, or write outcome is unverified, stop the workflow and surface a redacted error to the owner.

Resources

Next Steps

Apply patterns in appfolio-core-workflow-a.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

jeremylongshore/tons-of-skills-marketplace

Use when asked to structure a landing page for positioning, plan a conversion-optimized page layout, or design a launch page. Examples: "landing page for product launch", "conversion-optimized layout for SaaS

jeremylongshore/tons-of-skills-marketplace

Use when asked to design growth-optimized landing pages, activation funnel layouts, or experiment-friendly page structures. Examples: "growth-optimized landing", "activation funnel layout", "A/B testable page

jeremylongshore/tons-of-skills-marketplace

Use when asked to structure a landing page, design page layout for conversion, or plan landing page information architecture. Examples: "landing page structure for SaaS", "conversion-optimized layout

jeremylongshore/tons-of-skills-marketplace

Generate polished demo videos from a single prompt. Use when the user asks to create a demo video, product walkthrough, feature showcase, or animated presentation. Trigger with "make a demo video", "create a product video", "demo walkthrough", or "feature showcase video".

jeremylongshore/tons-of-skills-marketplace

Track cryptocurrency portfolio with real-time valuations, allocation analysis, and P&L tracking. Use when checking portfolio value, viewing holdings breakdown, analyzing allocations, or exporting portfolio data. Trigger with phrases like "show my portfolio", "check crypto holdings", "portfolio allocation", "track my crypto", or "export portfolio".

jeremylongshore/tons-of-skills-marketplace

Runs the full release workflow for the current project. Commits any uncommitted changes, pushes to remote, creates and merges a PR if on a feature branch, determines the next semver version from conventional commits, creates an annotated git tag and GitHub release with generated notes, cleans up merged branches, and returns to a clean main. Use when the user says promote, ship, release, commit and push, tag and release, or get back to main.

jeremylongshore/tons-of-skills-marketplace

Agent skill at plugins/productivity/content-multiplier/skills/channel-formats/SKILL.md

jeremylongshore/tons-of-skills-marketplace

Operate an evidence-backed editorial queue, balance topic and format coverage, schedule approved drafts, and reconcile backlog state with the publishing system. Use when choosing or scheduling the next blog work. Trigger with "show the editorial calendar" or "pick the next topic".

jeremylongshore/tons-of-skills-marketplace

Configure CI/CD pipeline for AppFolio property management integrations. Trigger: "appfolio CI".

jeremylongshore/tons-of-skills-marketplace

Diagnose and fix common AppFolio API integration errors. Trigger: "appfolio error".

jeremylongshore/tons-of-skills-marketplace

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

jeremylongshore/tons-of-skills-marketplace

Automate tenant management and lease operations with AppFolio. Trigger: "appfolio tenant management".

jeremylongshore/tons-of-skills-marketplace

Collect AppFolio API debug evidence for support tickets. Trigger: "appfolio debug".

jeremylongshore/tons-of-skills-marketplace

Deploy AppFolio integration service to cloud infrastructure. Trigger: "deploy appfolio".

jeremylongshore/tons-of-skills-marketplace

Query AppFolio properties, units, and tenants via REST API. Trigger: "appfolio hello world".

jeremylongshore/tons-of-skills-marketplace

Configure AppFolio Stack API authentication with OAuth 2.0. Use when setting up property management API access, registering as an AppFolio Stack partner, or configuring client credentials for API calls. Trigger: "install appfolio", "setup appfolio", "appfolio auth", "appfolio API key".

jeremylongshore/tons-of-skills-marketplace

Set up local development for AppFolio property management API integration. Trigger: "appfolio local dev".

jeremylongshore/tons-of-skills-marketplace

Production readiness checklist for AppFolio integrations. Trigger: "appfolio production checklist".

jeremylongshore/tons-of-skills-marketplace

Handle AppFolio API rate limits with throttling and backoff. Trigger: "appfolio rate limit".

jeremylongshore/tons-of-skills-marketplace

Reference architecture for AppFolio property management integration. Trigger: "appfolio architecture".

Skills associés