Aptoria
Sign in
Start free
Developers
API + webhook reference.
Pull rent data into your accounting tool, push work orders from your scheduler, or react in real time to events with HMAC-signed webhooks. Generate keys from Settings → Developer.
On this page
Introduction
Authentication
Errors & rate limits
Endpoints
Webhooks
Verifying signatures
SDKs & examples
Support
Get your API key →
Introduction
The Aptoria REST API uses JSON over HTTPS. Base URL: https://app.aptoria.ai. All authenticated endpoints live under /api/* and accept JSON bodies on POST/PATCH.
The same endpoints power the app itself — anything you can do in the UI you can do via the API, subject to the scopes on your API key.
Authentication
Issue a key from Settings → Developer. Keys start with apt_ and are shown exactly once — store them securely.
Send the key as a Bearer token in the Authorization header on every request:
bash
Copy
curl https://app.aptoria.ai/api/dashboard \ -H "Authorization: Bearer apt_a1b2c3d4_BASE64URL_SECRET"
Each key is scoped (e.g., read:tenants, write:work_orders). A request that hits an endpoint your scopes don't cover returns 403.
Errors & rate limits
Errors return JSON with an error field and a 4xx/5xx status:
json
Copy
{ "error": "Rate limit exceeded — try again in 47 seconds." }
Defaults: 60 requests/minute per key, 1000/hour. Heavy AI endpoints have tighter per-key quotas (e.g., 10/h for delinquency prioritizer). The Retry-After response header tells you when to retry.
Endpoints
List delinquent rent
bash
Copy
curl https://app.aptoria.ai/api/ar-aging \ -H "Authorization: Bearer apt_..."
json
Copy
{ "rows": [ { "payment_id": "p_01H...", "tenant_name": "Maya R.", "property_name": "127 Pine", "unit_label": "1B", "amount": 1825, "due_date": "2026-04-01", "days_late": 54 } ], "totals": { "0-30": 1825, "31-60": 1825, "61-90": 0, "90+": 0, "total": 3650 } }
Post an expense
bash
Copy
curl -X POST https://app.aptoria.ai/api/expenses \ -H "Authorization: Bearer apt_..." \ -H "Content-Type: application/json" \ -d '{ "action": "add_entry", "property_id": "5f...", "category_id": "repairs", "label": "Snow removal — driveway", "amount_usd": 380, "paid_on": "2026-02-14", "vendor": "Twin Cities Plow Co." }'
List work orders for a unit
bash
Copy
curl "https://app.aptoria.ai/api/work-orders?unit_id=5f..." \ -H "Authorization: Bearer apt_..."
Trigger an AI helper
bash
Copy
curl -X POST https://app.aptoria.ai/api/ai-vendor-summary \ -H "Authorization: Bearer apt_..." \ -H "Content-Type: application/json" \ -d '{ "vendor_id": "v_01H..." }'
Webhooks
Register endpoints under Settings → Developer. Each delivery is a POST with the event payload and a signature header. Twelve event kinds today:
rent.paid
rent.late
application.submitted
application.approved
application.denied
lease.signed
lease.terminated
work_order.created
work_order.completed
tenant.added
tenant.removed
inspection.completed
payment_plan.proposed
Example payload
json
Copy
{ "event": "rent.paid", "delivered_at": "2026-05-25T15:42:18.224Z", "organization_id": "org_01H...", "payload": { "payment_id": "p_01H...", "lease_id": "l_01H...", "amount_usd": 1825, "period_label": "2026-05", "paid_at": "2026-05-25T15:42:14Z" } }
We retry failed deliveries with exponential backoff for ~48 hours. After 10 consecutive failures the endpoint is auto-disabled and the owner gets an email.
Verifying signatures
Every webhook POST includes X-PMAI-Signature:
shell
Copy
X-PMAI-Signature: t=1716659938,v1=4f1c2b...
t is the unix-second timestamp; v1 is an HMAC-SHA256 of t.body using your endpoint's signing secret. Reject deliveries where the timestamp is more than 5 minutes old (replay protection) or where the HMAC doesn't match.
javascript
Copy
// Node — express import crypto from 'crypto'; app.post('/webhooks/pmai', express.text({ type: '*/*' }), (req, res) => { const sig = req.header('X-PMAI-Signature') || ''; const parts = Object.fromEntries(sig.split(',').map((p) => p.split('='))); const t = Number(parts.t), v1 = parts.v1; if (!t || !v1) return res.status(400).end(); if (Math.abs(Date.now() / 1000 - t) > 300) return res.status(400).end(); const mac = crypto.createHmac('sha256', process.env.PMAI_WEBHOOK_SECRET) .update(`${t}.${req.body}`) .digest('hex'); if (!crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(v1))) { return res.status(401).end(); } const event = JSON.parse(req.body); // ...handle event res.status(200).end(); });
python
Copy
# Python — Flask import hmac, hashlib, os, time, json from flask import request, abort @app.post('/webhooks/pmai') def pmai(): sig = request.headers.get('X-PMAI-Signature', '') parts = dict(p.split('=') for p in sig.split(',')) t = int(parts.get('t', 0)); v1 = parts.get('v1', '') if abs(time.time() - t) > 300: abort(400) body = request.get_data(as_text=True) expected = hmac.new( os.environ['PMAI_WEBHOOK_SECRET'].encode(), f'{t}.{body}'.encode(), hashlib.sha256, ).hexdigest() if not hmac.compare_digest(expected, v1): abort(401) event = json.loads(body) # ...handle event return ('', 200)
SDKs & examples
No official SDK yet — the API is small enough that a thin fetch wrapper handles most use cases. Drop-in helper:
typescript
Copy
// pmai.ts const BASE = 'https://app.aptoria.ai'; export async function pmai<T = unknown>( path: string, init?: RequestInit & { json?: unknown } ): Promise<T> { const { json, headers, ...rest } = init || {}; const res = await fetch(`${BASE}${path}`, { ...rest, headers: { Authorization: `Bearer ${process.env.PMAI_API_KEY!}`, ...(json ? { 'Content-Type': 'application/json' } : {}), ...(headers || {}), }, body: json ? JSON.stringify(json) : (rest as any).body, }); if (!res.ok) throw new Error(`PMAI ${res.status}: ${await res.text()}`); return res.json(); } // Usage: const aging = await pmai('/api/ar-aging'); await pmai('/api/expenses', { method: 'POST', json: { action: 'add_entry', ... } });
Support
Hit a wall? Email developers@aptoria.ai with the request ID from the response (header X-Request-Id) and we'll dig in. Production issues get same-business-day responses on paid plans.
Generate API key
Contact us
Aptoria
The modern operating system for rental portfolios. Built for landlords, by people who manage units.
© 2026 Aptoria · All rights reserved
Made with care for landlords + tenants alike.