Skip to main content

Manual Context

For most AI agents, pointing them at llms.txt is the easiest way to get started — that file is always up to date as new endpoints are released. See the AI Agents overview for the quick start.

This page is the reference for everything beyond the quick start:

API Context

If your agent can't fetch URLs — for example, a custom agent without web-fetch tools, or a deployment where outbound HTTP from the agent isn't permitted — paste the following block directly into your agent's context or system prompt instead.

Skywalk API — REST API for AppFolio Property Manager data.
Base URL: https://api.skywalkapi.com/v1/
Auth: X-API-Key header with your API key.
Rate limit: 1 req/sec.

Endpoints return JSON with { meta, links, data } structure.
meta.status can be "ok", "stale", or "updating".
If "updating", poll every 10 seconds until "ok".

Key GET endpoints:
/v1/properties, /v1/units, /v1/tenants, /v1/lease-history,
/v1/cash-flow, /v1/balance-sheet, /v1/rent-roll,
/v1/work-orders, /v1/vendors, /v1/tenant-ledger,
/v1/guest-cards, /v1/rental-applications

A pasted snippet is a point-in-time copy — re-check this page when adding new endpoints to your agent's context.

Tool Definitions

If you're building a custom AI agent using the Claude API or OpenAI API with tool use (function calling), here are ready-to-use tool definitions.

Claude Tool Use Format

[
{
"name": "skywalk_get_endpoint",
"description": "Call a Skywalk API GET endpoint to retrieve AppFolio property management data. Returns JSON with meta, links, and data fields. If meta.status is 'updating', the caller should wait 10 seconds and retry.",
"input_schema": {
"type": "object",
"properties": {
"endpoint": {
"type": "string",
"description": "The endpoint path, e.g. '/v1/properties', '/v1/tenants', '/v1/cash-flow'"
},
"query_params": {
"type": "object",
"description": "Optional query parameters as key-value pairs, e.g. { \"propertyIds\": \"123,456\" }"
}
},
"required": ["endpoint"]
}
}
]

OpenAI Function Calling Format

[
{
"type": "function",
"function": {
"name": "skywalk_get_endpoint",
"description": "Call a Skywalk API GET endpoint to retrieve AppFolio property management data. Returns JSON with meta, links, and data fields. If meta.status is 'updating', the caller should wait 10 seconds and retry.",
"parameters": {
"type": "object",
"properties": {
"endpoint": {
"type": "string",
"description": "The endpoint path, e.g. '/v1/properties', '/v1/tenants', '/v1/cash-flow'"
},
"query_params": {
"type": "object",
"description": "Optional query parameters as key-value pairs"
}
},
"required": ["endpoint"]
}
}
}
]

Handling the Async Polling Pattern

Skywalk API uses an async pattern: when data needs to be refreshed from AppFolio, the API returns immediately with meta.status: "updating" rather than making you wait. Your tool implementation should handle this with a simple poll loop.

Here's a reference implementation in Python:

import requests
import time

API_BASE = "https://api.skywalkapi.com"
API_KEY = "your-api-key"

def call_skywalk(endpoint, query_params=None):
"""Call a Skywalk API endpoint, handling the async polling pattern."""
headers = {"X-API-Key": API_KEY}
url = f"{API_BASE}{endpoint}"

while True:
response = requests.get(url, headers=headers, params=query_params)
data = response.json()

status = data.get("meta", {}).get("status")
if status == "ok":
return data
elif status in ("updating", "stale"):
time.sleep(10)
else:
return data # error or unexpected status

And in JavaScript/TypeScript:

async function callSkywalk(endpoint, queryParams = {}) {
const url = new URL(endpoint, "https://api.skywalkapi.com");
Object.entries(queryParams).forEach(([k, v]) => url.searchParams.set(k, v));

while (true) {
const res = await fetch(url, {
headers: { "X-API-Key": "your-api-key" },
});
const data = await res.json();

if (data.meta?.status === "ok") return data;
if (data.meta?.status === "updating" || data.meta?.status === "stale") {
await new Promise((r) => setTimeout(r, 10000));
continue;
}
return data;
}
}

Example Prompts

Once your AI agent is configured, here are practical things you can ask:

PromptWhat it does
"List all my properties and how many units each has"Calls /v1/properties and summarizes the results
"Show me vacant units across all properties"Calls /v1/unit-vacancies and formats the output
"Pull the cash flow statement for last month"Calls /v1/cash-flow with date parameters
"Who are my tenants with leases expiring in the next 60 days?"Calls /v1/lease-history and filters by expiration date
"Show me open work orders sorted by priority"Calls /v1/work-orders and sorts the results
"What's my current rent roll?"Calls /v1/rent-roll and summarizes totals
"Compare balance sheets for my top 3 properties"Calls /v1/balance-sheet for each property and compares