API Reference
PMFriend's HTTP API. Everything under /api/v1/* on
https://app.pmfriend.com.
Authentication
Two auth styles:
- Session cookie (PM-facing).
mypmf_sessionHS384 JWT set on/api/v1/auth/loginor/api/v1/auth/register. 14-day TTL, HttpOnly, Secure. Pass back on every request. - Magic-link token (contractor / tenant). Token in the URL path:
/api/v1/jobs/{token}. SHA-256-hashed server-side. No session cookie needed.
All data is scoped to the calling agency via PostgreSQL row-level
security. No explicit agencyId parameter — the database enforces the
boundary.
Auth
| Method | Path | Purpose |
|---|---|---|
| POST | /auth/register | Create agency + first admin. Returns session cookie. |
| POST | /auth/login | Email + password. Returns session cookie. |
| POST | /auth/logout | Clears session cookie (both Lax + Partitioned variants). |
| POST | /auth/demo-login | Drops caller into the Sandbox Agency. Sets Partitioned cookie for cross-site iframe. |
| GET | /auth/me | Returns current user + agency. |
Google SSO via Spring Security: /oauth2/authorization/google 302s to
Google, back to /login/oauth2/code/google.
Maintenance
| Method | Path | Purpose |
|---|---|---|
| GET | /maintenance-requests | List. Filter by status, urgency, property. |
| GET | /maintenance-requests/{id} | Single request |
| POST | /maintenance-requests | Create (MANUAL channel) |
| POST | /maintenance-requests/{id}/suggest-triage | AI triage, non-mutating |
| POST | /maintenance-requests/{id}/apply-triage | Commit category + urgency, starts SLA |
| POST | /maintenance-requests/{id}/detect-duplicate | AI duplicate check |
| POST | /maintenance-requests/{id}/convert-to-work-order | Convert with scope + cost ceiling |
| POST | /report/{token} | Public tenant submission (no auth) |
Work orders
| Method | Path | Purpose |
|---|---|---|
| GET | /work-orders | List |
| GET | /work-orders/{id} | Detail |
| POST | /work-orders/draft-scope | AI scope drafter, non-mutating |
| GET | /work-orders/{id}/suggested-contractors | Ranked contractor picker |
| POST | /work-orders/{id}/dispatch | Send magic link to contractor |
| GET | /jobs/{token} | Contractor view (public, token-gated) |
| POST | /jobs/{token}/transitions | Contractor status change |
| POST | /jobs/{token}/invoices | Contractor invoice upload |
Properties
| Method | Path | Purpose |
|---|---|---|
| GET | /properties | List |
| GET | /properties/{id} | Detail |
| POST | /properties | Create |
| PATCH | /properties/{id} | Update |
| GET | /properties/{id}/ai-suggestions | AI-derived next actions |
| POST | /properties/{id}/ai-actions/create-work-order | One-click chain: submit → triage → scope → WO |
| GET | /properties/{id}/compliance | Compliance register |
| POST | /properties/import-csv | Bulk import (multipart) |
Contractors
| Method | Path | Purpose |
|---|---|---|
| GET | /contractors | List |
| GET | /contractors/{id} | Detail |
| POST | /contractors | Create |
| PATCH | /contractors/{id} | Update |
| POST | /contractors/import-csv | Bulk import (multipart) |
Owners
| Method | Path | Purpose |
|---|---|---|
| GET | /owners | List |
| GET | /owners/{id} | Detail |
| POST | /owners | Create |
| POST | /owners/{id}/digests/draft | AI digest drafter |
| GET | /owners/{id}/digests | Sent-digest log |
Tenants
| Method | Path | Purpose |
|---|---|---|
| GET | /tenants | List |
| POST | /tenants | Create |
| POST | /tenants/import-csv | Bulk import (multipart) |
Compliance
| Method | Path | Purpose |
|---|---|---|
| GET | /compliance/tasks | All tasks across agency |
| POST | /compliance/tasks/{id}/complete | Mark done, auto-schedules next |
Legal notices
All non-mutating — drafts are returned to the caller; nothing is persisted.
| Method | Path | Purpose |
|---|---|---|
| POST | /notices/draft-arrears | State-aware rent-arrears notice (friendly reminder / Notice to Remedy Breach / Notice of Termination). See Arrears Ladder |
| POST | /notices/draft-entry-notice | State-aware entry notice with required statutory notice period per reason. See Entry Notice |
| POST | /case-packs/draft | Tribunal/VCAT case pack: pulls maintenance + work-order history at a property within a date range, returns Statement of Facts + chronology + relief. See Case Pack |
| POST | /inspections/draft | Routine inspection report: PM-supplied room-by-room checklist → AU-English prose + extracted safety flags + follow-ups. See Inspection Report Writer |
Notifications
| Method | Path | Purpose |
|---|---|---|
| GET | /notifications?scope=MINE|ALL&unreadOnly=&limit= | Per-user feed. MINE (default) returns assigned-to-me + agency-wide; ALL returns every row in the agency |
| POST | /notifications/{id}/read | Mark read for the calling user |
| POST | /notifications/{id}/dismiss | Dismiss for the calling user |
| POST | /notifications/read-all?scope=MINE|ALL | Mark every visible-and-unread notification read for the calling user, scoped per scope |
Settings & Team
| Method | Path | Purpose |
|---|---|---|
| GET | /agencies/me | Current agency profile |
| PATCH | /agencies/me | Update name, ABN, logoUrl |
| GET | /team-invites | Pending invite list |
| POST | /team-invites | Issue invite |
| DELETE | /team-invites/{id} | Cancel invite |
| POST | /team-invites/accept | Accept invite with password set |
Rate limits
/report/{token}— 5 submissions per token per hour/jobs/{token}/*— 30 requests per token per minute- Authenticated endpoints — 300 requests per minute per session
Response format
JSON throughout. Errors look like:
{
"error": "validation_failed",
"message": "Human-readable summary",
"details": [
{ "field": "ownerApprovalThresholdCents", "message": "must be positive" }
]
}
HTTP status codes mean what they say on the tin (400 validation, 401 auth, 403 forbidden, 404 not found, 409 conflict, 429 rate-limited, 5xx server).
Versioning
/api/v1/* is the current namespace. Breaking changes get /api/v2/*.
Non-breaking additions (new fields, new endpoints) happen in-place.
See also
- AI Overview — which endpoints hit Claude
- Magic-Link Dispatch — token format