API Documentation
REST API v1 — Integrate SystoMax into your mobile apps, websites, and third-party systems. 245 routes across 6 personas.
Table of Contents
Quick start
Authenticate once, keep the returned token, and send it as a Bearer token on every subsequent request. All examples use placeholders — replace the host, credentials, and token with your own values.
https://your-society.example.com/api/v1Send
Accept: application/json and Content-Type: application/json.Society tokens: 1 year. PMF tokens: 30 days.
curl -X POST "https://your-society.example.com/api/v1/auth/login" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@example.com",
"password": "YOUR_PASSWORD",
"code": "green-valley-a1b2"
}'
const response = await fetch("https://your-society.example.com/api/v1/auth/login", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
email: "admin@example.com",
password: "YOUR_PASSWORD",
code: "green-valley-a1b2"
})
});
const data = await response.json();
console.log(data.token);
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()->post('https://your-society.example.com/api/v1/auth/login', [
'email' => 'admin@example.com',
'password' => 'YOUR_PASSWORD',
'code' => 'green-valley-a1b2',
]);
$token = $response->throw()->json('token');
{
"token": "YOUR_SOCIETY_API_TOKEN",
"user": {
"id": 42,
"name": "Jane Doe",
"email": "admin@example.com",
"type": "admin"
},
"tenant": {
"id": 5,
"society_name": "Green Valley Housing"
}
}
curl "https://your-society.example.com/api/v1/society/dashboard" \ -H "Authorization: Bearer YOUR_SOCIETY_API_TOKEN" \ -H "Accept: application/json"
const response = await fetch("https://your-society.example.com/api/v1/society/dashboard", {
headers: {
"Authorization": "Bearer YOUR_SOCIETY_API_TOKEN",
"Accept": "application/json"
}
});
const dashboard = await response.json();
console.log(dashboard.total_units);
use Illuminate\Support\Facades\Http;
$response = Http::withToken('YOUR_SOCIETY_API_TOKEN')
->acceptJson()
->get('https://your-society.example.com/api/v1/society/dashboard');
$dashboard = $response->throw()->json();
{
"total_units": 120,
"occupied_units": 96,
"vacant_units": 24,
"open_complaints": 3,
"total_residents": 148,
"pending_invoices": 425000,
"collected_amount": 12850000
}
1. Authentication
Public endpoints — no token required. Use /auth/login to obtain a society Bearer API token, or /auth/pmf-login to obtain a PMF API token. Tokens must be sent on every authenticated request.
/api/v1/auth/login Obtain a society Bearer tokenRequest Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | User email address | |
| password | string | Yes | User password |
code | string | No* | Tenant code or tenant ID. Required when the request host does not already resolve a society. |
persona | string | No | staff, resident, or security. Validates the account type. |
* On a mapped society domain, the tenant is resolved from the host and code may be omitted. On a shared or central host, send code.
/api/v1/auth/pmf-login Obtain a PMF Bearer tokenRequest Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | User email address | |
| password | string | Yes | User password |
code | string | Yes | PMF code. |
persona | string | Yes | staff, owner, or tenant. |
PMF tokens are valid for 30 days. Tokens with an owner or tenant persona cannot call staff endpoints.
curl -X POST "https://your-pmf.example.com/api/v1/auth/pmf-login" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"email": "owner@example.com",
"password": "YOUR_PASSWORD",
"code": "the-property-co",
"persona": "owner"
}'
const response = await fetch("https://your-pmf.example.com/api/v1/auth/pmf-login", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
email: "owner@example.com",
password: "YOUR_PASSWORD",
code: "the-property-co",
persona: "owner"
})
});
const data = await response.json();
console.log(data.token);
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()->post('https://your-pmf.example.com/api/v1/auth/pmf-login', [
'email' => 'owner@example.com',
'password' => 'YOUR_PASSWORD',
'code' => 'the-property-co',
'persona' => 'owner',
]);
$token = $response->throw()->json('token');
{
"token": "YOUR_PMF_API_TOKEN",
"user": {
"id": 17,
"name": "Alex Owner",
"email": "owner@example.com",
"type": "owner",
"persona": "owner"
},
"pmf": {
"id": 4,
"company_name": "The Property Co"
}
}
/auth/register — Register new user (requires feature: allow_signup)Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Full name |
| string | Yes | Email address | |
| password | string | Yes | Password (min 8 characters) |
society_name | string | Yes | Name of the society to create. |
phone | string | No | Contact phone number (max 20 characters). |
society_type | string | No | One of housing_society, apartment_complex, commercial_plaza, gated_community, mixed_community. |
plan_code | string | No | Active plan code. Defaults to the starter plan. |
201 Created returns the new tenant, an admin user, and a token. Requires the allow_signup feature flag.
/auth/me — Get authenticated user profileReturns the authenticated society user's profile. Requires Authorization: Bearer YOUR_SOCIETY_API_TOKEN.
/auth/logout — Revoke current tokenRevokes the current society API token. Requires Authorization: Bearer YOUR_SOCIETY_API_TOKEN.
2. Society API read-only
Read-only endpoints for society data. Authenticate with Authorization: Bearer YOUR_SOCIETY_API_TOKEN. All queries are scoped to the authenticated tenant.
curl "https://your-society.example.com/api/v1/society/units" \ -H "Authorization: Bearer YOUR_SOCIETY_API_TOKEN" \ -H "Accept: application/json"
const response = await fetch("https://your-society.example.com/api/v1/society/units", {
headers: {
"Authorization": "Bearer YOUR_SOCIETY_API_TOKEN",
"Accept": "application/json"
}
});
const units = await response.json();
console.log(units);
use Illuminate\Support\Facades\Http;
$units = Http::withToken('YOUR_SOCIETY_API_TOKEN')
->acceptJson()
->get('https://your-society.example.com/api/v1/society/units')
->throw()
->json();
[
{
"id": 101,
"unit_number": "A-101",
"block_name": "Block A",
"status": "occupied",
"monthly_rent": 45000
},
{
"id": 102,
"unit_number": "A-102",
"block_name": "Block A",
"status": "vacant",
"monthly_rent": 42000
}
]
/society/dashboard
— Dashboard summary — units, residents, invoices, complaints, bookings
/society/units
— List all units — flat array, no pagination or filters
/society/residents
— List all residents — flat array, no pagination or filters
/society/invoices
— List invoices (?status=, ?unit_id=; paginated 20/page)
/society/payments
— List payment records — paginated 20/page, no filters
/society/complaints
— List complaints — paginated 20/page, no filters
/society/announcements
— List active announcements — flat array
/society/visitors
— List visitors — paginated 20/page, no filters
/society/bookings
— List facility bookings — paginated 20/page, no filters
/society/reports/summary
— Dashboard summary report
/society/polls
— List active polls
/society/polls/{id}
— Get poll details with options
/society/polls/{id}/vote
— Cast vote on a poll
/society/polls/{id}/results
— Get poll results
3. Society Staff API staff
Full CRUD for society staff. Same society Bearer token. Prefix: /api/v1/society/staff
curl "https://your-society.example.com/api/v1/society/staff/dashboard" \ -H "Authorization: Bearer YOUR_SOCIETY_API_TOKEN" \ -H "Accept: application/json"
const response = await fetch("https://your-society.example.com/api/v1/society/staff/dashboard", {
headers: {
"Authorization": "Bearer YOUR_SOCIETY_API_TOKEN",
"Accept": "application/json"
}
});
const dashboard = await response.json();
use Illuminate\Support\Facades\Http;
$dashboard = Http::withToken('YOUR_SOCIETY_API_TOKEN')
->acceptJson()
->get('https://your-society.example.com/api/v1/society/staff/dashboard')
->throw()
->json();
{
"total_units": 120,
"occupied_units": 96,
"vacant_units": 24,
"open_complaints": 3,
"open_maintenance": 7,
"pending_invoices": 425000,
"collected_ytd": 12850000,
"total_residents": 148,
"today_visitors": 12,
"upcoming_bookings": 4
}
curl -X POST "https://your-society.example.com/api/v1/society/staff/payments" \
-H "Authorization: Bearer YOUR_SOCIETY_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"invoice_id": 501,
"amount": 45000,
"payment_method": "cash",
"reference": "REC-2026-001"
}'
const response = await fetch("https://your-society.example.com/api/v1/society/staff/payments", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_SOCIETY_API_TOKEN",
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
invoice_id: 501,
amount: 45000,
payment_method: "cash",
reference: "REC-2026-001"
})
});
const payment = await response.json();
use Illuminate\Support\Facades\Http;
$payment = Http::withToken('YOUR_SOCIETY_API_TOKEN')
->acceptJson()
->post('https://your-society.example.com/api/v1/society/staff/payments', [
'invoice_id' => 501,
'amount' => 45000,
'payment_method' => 'cash',
'reference' => 'REC-2026-001',
])->throw()->json();
{
"id": 9001,
"message": "Payment recorded"
}
/society/staff/dashboard
— Staff dashboard — counts, alerts, recent activities
/society/staff/buildings
— List buildings
/society/staff/blocks
— List blocks
/society/staff/phases
— List phases
/society/staff/unit-categories
— List unit categories
/society/staff/plot-categories
— List plot categories
/society/staff/units
— List units (?status=, ?building_id=, ?search=)
/society/staff/units/{id}/status
— Update unit status
/society/staff/plots
— List plots
/society/staff/residents
— List residents (?search=, ?per_page=)
/society/staff/staff
— List staff users
/society/staff/roles
— List roles with permissions
/society/staff/invoices
— List invoices (?status=, ?type=, ?unit_id=, ?from=, ?to=)
/society/staff/invoices
— Create invoice
/society/staff/invoices/{id}/status
— Update invoice status
/society/staff/payments
— List payments (?status=, ?method=)
/society/staff/payments
— Record payment
/society/staff/complaints
— List complaints (?status=, ?priority=)
/society/staff/complaints/{id}/status
— Update complaint status
/society/staff/maintenance
— List maintenance requests (?status=, ?type=)
/society/staff/maintenance/{id}/status
— Update maintenance status
/society/staff/visitors
— List visitors (?status=)
/society/staff/visitors
— Create visitor entry
/society/staff/visitors/{id}/status
— Update visitor status (approve/reject/check-in/check-out)
/society/staff/bookings
— List bookings (?status=, ?facility_id=)
/society/staff/bookings/{id}/status
— Approve/reject/cancel booking
/society/staff/facilities
— List facilities
/society/staff/announcements
— List announcements
/society/staff/announcements
— Create announcement
/society/staff/vendors
— List vendors
/society/staff/assets
— List assets
/society/staff/documents
— List documents
/society/staff/contracts
— List tenancy contracts
/society/staff/move-ins
— List move-in requests
/society/staff/move-ins/{id}/status
— Approve/reject move-in
/society/staff/parking-buildings
— List buildings with parking floors
/society/staff/notifications
— List notifications
/society/staff/notifications/{id}/read
— Mark notification as read
/society/staff/profile
— Get staff profile
/society/staff/profile
— Update staff profile
/society/staff/change-password
— Change password
4. Society Resident API portal
Customer portal for society residents/owners. Society Bearer token. Prefix: /api/v1/society/resident
curl "https://your-society.example.com/api/v1/society/resident/dashboard" \ -H "Authorization: Bearer YOUR_SOCIETY_API_TOKEN" \ -H "Accept: application/json"
const response = await fetch("https://your-society.example.com/api/v1/society/resident/dashboard", {
headers: {
"Authorization": "Bearer YOUR_SOCIETY_API_TOKEN",
"Accept": "application/json"
}
});
const dashboard = await response.json();
use Illuminate\Support\Facades\Http;
$dashboard = Http::withToken('YOUR_SOCIETY_API_TOKEN')
->acceptJson()
->get('https://your-society.example.com/api/v1/society/resident/dashboard')
->throw()
->json();
{
"my_units": [
{
"id": 101,
"unit_number": "A-101",
"status": "occupied"
}
],
"open_complaints": 1,
"open_maintenance": 2,
"pending_invoices": 45000,
"upcoming_bookings": 1,
"announcements": [
{
"id": 8,
"title": "Water maintenance schedule",
"created_at": "2026-10-18T09:00:00.000000Z"
}
],
"active_polls": 2
}
curl -X POST "https://your-society.example.com/api/v1/society/resident/complaints" \
-H "Authorization: Bearer YOUR_SOCIETY_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"unit_id": 101,
"subject": "Water leakage in bathroom",
"description": "Water is leaking from the ceiling near the shower.",
"priority": "high"
}'
const response = await fetch("https://your-society.example.com/api/v1/society/resident/complaints", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_SOCIETY_API_TOKEN",
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
unit_id: 101,
subject: "Water leakage in bathroom",
description: "Water is leaking from the ceiling near the shower.",
priority: "high"
})
});
const complaint = await response.json();
use Illuminate\Support\Facades\Http;
$complaint = Http::withToken('YOUR_SOCIETY_API_TOKEN')
->acceptJson()
->post('https://your-society.example.com/api/v1/society/resident/complaints', [
'unit_id' => 101,
'subject' => 'Water leakage in bathroom',
'description' => 'Water is leaking from the ceiling near the shower.',
'priority' => 'high',
])->throw()->json();
{
"id": 301
}
/society/resident/dashboard
— Dashboard — my units, open complaints, pending invoices, announcements
/society/resident/profile
— Get profile with unit ownerships
/society/resident/profile
— Update profile (first_name, last_name, phone)
/society/resident/invoices
— List my invoices (?status=)
/society/resident/invoices/{id}
— Invoice detail with items and payments
/society/resident/payments
— List my payments (?status=)
/society/resident/contracts
— My tenancy contracts
/society/resident/complaints
— My complaints (?status=)
/society/resident/complaints/{id}
— Complaint detail with updates
/society/resident/complaints
— File a complaint
/society/resident/maintenance
— My maintenance requests (?status=)
/society/resident/maintenance/{id}
— Maintenance detail with work orders
/society/resident/maintenance
— Submit maintenance request
/society/resident/facilities
— List active facilities
/society/resident/facilities/check-availability
— Check facility availability (?facility_id=, ?date=)
/society/resident/bookings
— My bookings (?status=)
/society/resident/bookings
— Create a booking
/society/resident/bookings/{id}/cancel
— Cancel a booking
/society/resident/polls
— List active polls
/society/resident/polls/{id}/vote
— Vote on a poll
/society/resident/announcements
— List announcements
/society/resident/transfer-requests
— My transfer requests (?status=)
/society/resident/transfer-requests
— Submit transfer request
/society/resident/chain-of-title/{unitId}
— Chain of title for a unit
/society/resident/notifications
— List notifications
/society/resident/notifications/{id}/read
— Mark notification read
/society/resident/change-password
— Change password
5. PMF Owner API portal
Property owner portal for PMF. Send a PMF token via Authorization: Bearer YOUR_PMF_API_TOKEN or the X-PMF-Token header. Prefix: /api/v1/pmf/owner
curl "https://your-pmf.example.com/api/v1/pmf/owner/dashboard" \ -H "Authorization: Bearer YOUR_PMF_API_TOKEN" \ -H "Accept: application/json"
const response = await fetch("https://your-pmf.example.com/api/v1/pmf/owner/dashboard", {
headers: {
"Authorization": "Bearer YOUR_PMF_API_TOKEN",
"Accept": "application/json"
}
});
const dashboard = await response.json();
use Illuminate\Support\Facades\Http;
$dashboard = Http::withToken('YOUR_PMF_API_TOKEN')
->acceptJson()
->get('https://your-pmf.example.com/api/v1/pmf/owner/dashboard')
->throw()
->json();
{
"portfolio": {
"total_properties": 6,
"occupied_count": 5,
"vacant_count": 1,
"occupancy_rate": 83.3,
"total_monthly_rent": 270000
},
"financial": {
"collected_ytd": 2450000,
"pending_amount": 90000,
"maintenance_costs_ytd": 185000,
"open_maintenance": 2,
"open_complaints": 1,
"net_operating_income": 2265000
},
"health_score": {
"score": 88,
"grade": "Good",
"color": "#10b981",
"occupancy_score": 83.3,
"collection_score": 92.5,
"noi_score": 75,
"ar_score": 100
},
"yoy_comparison": {
"collected_ytd": 2450000,
"collected_last_ytd": 2180000,
"change": 270000,
"change_pct": 12.4
},
"quick_snapshot": {
"vacancy_cost": 45000,
"collection_rate": 92.5,
"open_maintenance": 2,
"pending_amount": 90000
},
"ar_aging": {
"current": 45000,
"overdue30": 0,
"overdue60": 0,
"overdue90": 0,
"overdue90plus": 0
},
"monthly_trends": {
"labels": ["Aug 2026", "Sep 2026"],
"collected": [220000, 270000],
"maintenance": [12000, 8000],
"billed": [270000, 270000],
"noi": [208000, 262000]
},
"expense_breakdown": [
{ "type": "Plumbing", "total": 8000 }
],
"lease_pipeline": [
{ "month": "Oct 2026", "count": 1 }
],
"cash_flow_projection": {
"monthly_rent": 270000,
"month1": 270000,
"month2": 270000,
"month3": 270000,
"total_income": 810000,
"estimated_expenses": 25000,
"net_projected": 785000
},
"tenant_payment_behavior": [
{ "unit_number": "A-101", "on_time": 10, "late": 1, "total": 11, "on_time_rate": 90.9 }
],
"property_performance": [
{
"unit_number": "A-101",
"status": "occupied",
"monthly_rent": 45000,
"collected_ytd": 495000,
"maintenance_ytd": 20000,
"noi_ytd": 475000,
"tenant_name": "Jane Tenant"
}
],
"recent_activity": {
"payments": [],
"upcoming_expiries": [],
"recent_messages": [],
"notifications": [],
"documents": []
}
}
/pmf/owner/dashboard
— Full dashboard — portfolio, financial, health score, AR aging, trends, behaviors
/pmf/owner/units
— List owned units with tenant + outstanding info
/pmf/owner/invoices
— List invoices (?status=, ?search=)
/pmf/owner/invoices/{id}
— Invoice detail with items and payments
/pmf/owner/maintenance
— List maintenance requests (?status=)
/pmf/owner/maintenance/{id}
— Maintenance detail with work orders
/pmf/owner/payments
— List payments (?status=)
/pmf/owner/complaints
— List my complaints (?status=)
/pmf/owner/complaints/{id}
— Complaint detail with updates
/pmf/owner/messages
— Staff-to-owner messages
/pmf/owner/notifications
— List notifications
/pmf/owner/documents
— List shared documents
/pmf/owner/profile
— Get profile with owned units
/pmf/owner/profile
— Update profile (first_name, last_name, phone)
/pmf/owner/change-password
— Change password
/pmf/owner/rental-income
— Rental income report (?year=) — monthly billed/collected/outstanding
/pmf/owner/budget-vs-actual
— Budget vs actual comparison (?year=)
/pmf/owner/occupancy-report
— Occupancy stats — by building, rates
/pmf/owner/security-deposits
— Security deposit summary
/pmf/owner/maintenance-expenses
— Maintenance expenses by type/month (?year=)
/pmf/owner/rent-roll
— Full rent roll — all units with tenant, rent, outstanding
/pmf/owner/valuation-history
— Valuation history (?unit_id=)
/pmf/owner/reports
— Reports (?type=summary|income|expense|occupancy, ?from=, ?to=)
/pmf/owner/search
— Search (?q=) — units, invoices, payments, transactions
/pmf/owner/export
— Export all data as JSON
/pmf/owner/polls
— List active polls
/pmf/owner/polls/{id}
— Poll detail with options
/pmf/owner/polls/{id}/vote
— Vote on poll
/pmf/owner/polls/{id}/results
— Poll results
6. PMF Staff API staff
Full PMF administration endpoints. Auth via PMF API token. Prefix: /api/v1/pmf/staffThis is the largest endpoint group covering all PMF modules
curl "https://your-pmf.example.com/api/v1/pmf/staff/dashboard" \ -H "Authorization: Bearer YOUR_PMF_API_TOKEN" \ -H "Accept: application/json"
const response = await fetch("https://your-pmf.example.com/api/v1/pmf/staff/dashboard", {
headers: {
"Authorization": "Bearer YOUR_PMF_API_TOKEN",
"Accept": "application/json"
}
});
const dashboard = await response.json();
use Illuminate\Support\Facades\Http;
$dashboard = Http::withToken('YOUR_PMF_API_TOKEN')
->acceptJson()
->get('https://your-pmf.example.com/api/v1/pmf/staff/dashboard')
->throw()
->json();
{
"total_units": 150,
"occupied_units": 118,
"vacant_units": 32,
"total_owners": 94,
"active_tenants": 118,
"open_complaints": 5,
"pending_maintenance": 9,
"pending_invoices": 1240000,
"collected_ytd": 18600000,
"pending_bookings": 3,
"employees": 27,
"recent_activities": {
"payments": [],
"complaints": [],
"bookings": []
}
}
curl -X POST "https://your-pmf.example.com/api/v1/pmf/staff/invoices" \
-H "Authorization: Bearer YOUR_PMF_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"unit_id": 86,
"user_id": 211,
"invoice_number": "INV-2026-0042",
"type": "rent",
"total": 60000,
"due_amount": 60000,
"due_date": "2026-11-05",
"description": "November 2026 rent",
"items": [
{ "description": "Monthly rent", "amount": 60000, "quantity": 1 }
]
}'
const response = await fetch("https://your-pmf.example.com/api/v1/pmf/staff/invoices", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_PMF_API_TOKEN",
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
unit_id: 86,
user_id: 211,
invoice_number: "INV-2026-0042",
type: "rent",
total: 60000,
due_amount: 60000,
due_date: "2026-11-05",
description: "November 2026 rent",
items: [
{ description: "Monthly rent", amount: 60000, quantity: 1 }
]
})
});
const invoice = await response.json();
use Illuminate\Support\Facades\Http;
$invoice = Http::withToken('YOUR_PMF_API_TOKEN')
->acceptJson()
->post('https://your-pmf.example.com/api/v1/pmf/staff/invoices', [
'unit_id' => 86,
'user_id' => 211,
'invoice_number' => 'INV-2026-0042',
'type' => 'rent',
'total' => 60000,
'due_amount' => 60000,
'due_date' => '2026-11-05',
'description' => 'November 2026 rent',
'items' => [
['description' => 'Monthly rent', 'amount' => 60000, 'quantity' => 1],
],
])->throw()->json();
{
"id": 701
}
/pmf/staff/dashboard
— Staff dashboard — summary counts, recent activities
/pmf/staff/buildings
— List buildings
/pmf/staff/buildings/{id}
— Building detail with floors and unit count
/pmf/staff/buildings
— Create building
/pmf/staff/buildings/{id}
— Update building
/pmf/staff/buildings/{id}
— Delete building
/pmf/staff/units
— List units (?status=, ?building_id=, ?search=)
/pmf/staff/units/{id}
— Unit detail — owners, tenant, outstanding, maintenance
/pmf/staff/units
— Create unit
/pmf/staff/units/{id}
— Update unit
/pmf/staff/owners
— List owners (?search=)
/pmf/staff/tenants
— List tenants (?status=, ?search=)
/pmf/staff/parking/floors
— List parking floors (?building_id=)
/pmf/staff/parking/slots
— List parking slots (?floor_id=, ?status=)
/pmf/staff/parking/assignments
— List parking assignments (?unit_id=, ?status=)
/pmf/staff/valuations
— List valuations (?unit_id=)
/pmf/staff/valuations
— Create valuation
/pmf/staff/move-ins
— List move-in requests (?status=)
/pmf/staff/move-ins
— Create move-in request
/pmf/staff/move-ins/{id}/approve
— Approve move-in
/pmf/staff/move-outs
— List move-out requests (?status=)
/pmf/staff/move-outs
— Create move-out request
/pmf/staff/contracts
— List tenancy contracts (?status=)
/pmf/staff/contracts/{id}
— Contract detail with invoices
/pmf/staff/invoices
— List invoices (?status=, ?type=, ?unit_id=, ?search=)
/pmf/staff/invoices/{id}
— Invoice detail with items and payments
/pmf/staff/invoices
— Create invoice with line items
/pmf/staff/invoices/{id}/status
— Update invoice status
/pmf/staff/payments
— List payments (?status=, ?method=, ?unit_id=)
/pmf/staff/payments/{id}
— Payment detail
/pmf/staff/payments
— Record payment (auto-decrements invoice)
/pmf/staff/pdcs
— List post-dated cheques (?status=)
/pmf/staff/pdcs
— Add PDC
/pmf/staff/pdcs/{id}/status
— Update PDC status (cleared/bounced)
/pmf/staff/management-fees
— List management fees (?status=)
/pmf/staff/management-fees
— Add management fee
/pmf/staff/insurance
— List insurance policies
/pmf/staff/insurance
— Add insurance policy
/pmf/staff/journal-entries
— List journal entries (?type=)
/pmf/staff/journal-entries
— Create journal entry
/pmf/staff/chart-of-accounts
— List chart of accounts
/pmf/staff/employees
— List employees (?status=, ?department_id=, ?search=)
/pmf/staff/employees
— Create employee
/pmf/staff/employees/{id}
— Employee detail with attendance, leaves, advances, payroll
/pmf/staff/departments
— List departments
/pmf/staff/departments
— Create department
/pmf/staff/designations
— List designations
/pmf/staff/designations
— Create designation
/pmf/staff/attendance
— List attendance (?employee_id=, ?date=, ?status=)
/pmf/staff/attendance
— Record attendance
/pmf/staff/leaves
— List leaves (?employee_id=, ?status=)
/pmf/staff/leaves
— Request leave
/pmf/staff/leaves/{id}/status
— Approve/reject leave
/pmf/staff/advances
— List advances (?employee_id=, ?status=)
/pmf/staff/advances
— Request advance
/pmf/staff/advances/{id}/status
— Update advance status
/pmf/staff/payroll
— List payroll (?employee_id=, ?month=, ?status=)
/pmf/staff/payroll
— Process payroll
/pmf/staff/payroll/{id}/status
— Update payroll status
/pmf/staff/facilities
— List facilities
/pmf/staff/facilities
— Create facility
/pmf/staff/bookings
— List bookings (?status=, ?facility_id=)
/pmf/staff/bookings/{id}/status
— Approve/reject/cancel booking
/pmf/staff/complaints
— List complaints (?status=, ?priority=)
/pmf/staff/complaints/{id}
— Complaint detail with updates
/pmf/staff/complaints/{id}/status
— Update complaint status
/pmf/staff/complaints/{id}/assign
— Assign complaint to staff
/pmf/staff/maintenance
— List maintenance (?status=, ?type=)
/pmf/staff/maintenance/{id}
— Maintenance detail with work orders
/pmf/staff/maintenance/{id}/status
— Update maintenance status
/pmf/staff/polls
— List polls (?status=)
/pmf/staff/polls
— Create poll with options
/pmf/staff/polls/{id}
— Poll detail with vote count
/pmf/staff/visitors
— List visitors (?status=)
/pmf/staff/visitors
— Create visitor entry
/pmf/staff/visitors/{id}/status
— Update visitor status
/pmf/staff/assets
— List assets (?status=, ?category=)
/pmf/staff/assets
— Create asset
/pmf/staff/vendors
— List vendors
/pmf/staff/vendors
— Create vendor
/pmf/staff/users
— List users (?status=, ?role_id=, ?search=)
/pmf/staff/roles
— List roles
/pmf/staff/permissions
— List all permissions
/pmf/staff/cms/pages
— List CMS pages
/pmf/staff/cms/pages
— Create CMS page
/pmf/staff/cms/menus
— List CMS menus
/pmf/staff/cms/menus
— Create CMS menu item
/pmf/staff/cms/banners
— List banners
/pmf/staff/cms/gallery
— List gallery images
/pmf/staff/cms/events
— List events (?upcoming=1)
/pmf/staff/cms/enquiries
— List contact enquiries
/pmf/staff/cms/branding
— Get branding settings
/pmf/staff/cms/branding
— Update branding settings
/pmf/staff/cms/settings
— All CMS settings by group
/pmf/staff/reports
— Reports (?type=summary|income|occupancy|maintenance|complaints, ?from=, ?to=)
/pmf/staff/notifications
— List notifications
/pmf/staff/notifications
— Send notification
/pmf/staff/notifications/{id}/read
— Mark notification read
/pmf/staff/settings
— Get PMF settings
/pmf/staff/settings
— Update PMF settings
/pmf/staff/profile
— Get staff profile
/pmf/staff/change-password
— Change password
7. PMF Tenant API portal
Tenant portal for PMF societies. Auth via PMF API token. Prefix: /api/v1/pmf/tenant
curl "https://your-pmf.example.com/api/v1/pmf/tenant/dashboard" \ -H "Authorization: Bearer YOUR_PMF_API_TOKEN" \ -H "Accept: application/json"
const response = await fetch("https://your-pmf.example.com/api/v1/pmf/tenant/dashboard", {
headers: {
"Authorization": "Bearer YOUR_PMF_API_TOKEN",
"Accept": "application/json"
}
});
const dashboard = await response.json();
use Illuminate\Support\Facades\Http;
$dashboard = Http::withToken('YOUR_PMF_API_TOKEN')
->acceptJson()
->get('https://your-pmf.example.com/api/v1/pmf/tenant/dashboard')
->throw()
->json();
{
"tenant": {
"first_name": "Jane",
"last_name": "Tenant",
"email": "tenant@example.com",
"phone": "+92 300 1234567"
},
"my_units": [
{
"id": 86,
"unit_number": "A-101",
"building_name": "Tower A",
"status": "occupied"
}
],
"current_contract": {
"id": 44,
"contract_period_from": "2026-01-01",
"contract_period_to": "2026-12-31",
"rent_amount": 60000,
"unit_number": "A-101"
},
"pending_invoices": 60000,
"overdue_count": 0,
"open_complaints": 0,
"pending_maintenance": 1,
"upcoming_bookings": 1,
"unread_notifications": 3,
"announcements": []
}
curl -X POST "https://your-pmf.example.com/api/v1/pmf/tenant/maintenance" \
-H "Authorization: Bearer YOUR_PMF_API_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"unit_id": 86,
"type": "plumbing",
"title": "Kitchen sink blockage",
"description": "The kitchen sink drains very slowly.",
"priority": "high"
}'
const response = await fetch("https://your-pmf.example.com/api/v1/pmf/tenant/maintenance", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_PMF_API_TOKEN",
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
unit_id: 86,
type: "plumbing",
title: "Kitchen sink blockage",
description: "The kitchen sink drains very slowly.",
priority: "high"
})
});
const request = await response.json();
use Illuminate\Support\Facades\Http;
$request = Http::withToken('YOUR_PMF_API_TOKEN')
->acceptJson()
->post('https://your-pmf.example.com/api/v1/pmf/tenant/maintenance', [
'unit_id' => 86,
'type' => 'plumbing',
'title' => 'Kitchen sink blockage',
'description' => 'The kitchen sink drains very slowly.',
'priority' => 'high',
])->throw()->json();
{
"id": 812
}
/pmf/tenant/dashboard
— Dashboard — my units, contract, pending invoices, notifications, announcements
/pmf/tenant/profile
— Profile with current tenancy details
/pmf/tenant/profile
— Update profile (first_name, last_name, phone)
/pmf/tenant/tenancy
— Active tenancy contract with unit details
/pmf/tenant/invoices
— My invoices (?status=)
/pmf/tenant/invoices/{id}
— Invoice detail with items and payments
/pmf/tenant/payments
— My payment history (?status=)
/pmf/tenant/complaints
— My complaints (?status=)
/pmf/tenant/complaints/{id}
— Complaint detail with updates
/pmf/tenant/complaints
— File a complaint
/pmf/tenant/maintenance
— My maintenance requests (?status=)
/pmf/tenant/maintenance/{id}
— Maintenance detail with work orders
/pmf/tenant/maintenance
— Submit maintenance request
/pmf/tenant/facilities
— List active facilities
/pmf/tenant/bookings
— My bookings (?status=)
/pmf/tenant/bookings
— Create a booking
/pmf/tenant/bookings/{id}/cancel
— Cancel a booking
/pmf/tenant/documents
— View shared documents
/pmf/tenant/notifications
— List notifications
/pmf/tenant/notifications/{id}/read
— Mark notification read
/pmf/tenant/messages
— Staff-to-tenant messages
/pmf/tenant/messages
— Send message to staff
/pmf/tenant/polls
— List active polls
/pmf/tenant/polls/{id}/vote
— Vote on a poll
/pmf/tenant/change-password
— Change password
Response formats
All responses are JSON. Most list endpoints use Laravel's paginator envelope and accept ?page= and usually ?per_page= (capped at 100). Some endpoints, such as /society/units and /society/residents, return a flat JSON array — check the endpoint description.
Paginated collection
{
"current_page": 1,
"data": [
{ "id": 101, "unit_number": "A-101" }
],
"first_page_url": "https://your-society.example.com/api/v1/society/staff/units?page=1",
"from": 1,
"last_page": 3,
"last_page_url": "https://your-society.example.com/api/v1/society/staff/units?page=3",
"next_page_url": "https://your-society.example.com/api/v1/society/staff/units?page=2",
"path": "https://your-society.example.com/api/v1/society/staff/units",
"per_page": 20,
"prev_page_url": null,
"to": 20,
"total": 58
}
Single resource / mutation
{
"id": 101,
"unit_number": "A-101",
"status": "occupied"
}
// 201 Created
{
"id": 501
}
// Status change
{
"message": "Status updated"
}
Monetary values are numeric (not formatted strings). Dates use ISO 8601. Resource IDs are integers. Some PMF endpoints return {"data": [...]} instead of a paginator when the result is intentionally unpaginated.
8. Error Responses
Central and society endpoints use a message key. PMF resource controllers commonly use an error key. Validation failures always return 422 with an errors object.
| Status | Meaning |
|---|---|
400 | Missing tenant context or invalid PMF code. |
401 | Missing, invalid, revoked, or expired token. |
403 | Inactive account, persona mismatch, or insufficient permission. |
404 | Resource not found or not owned by the authenticated user. |
422 | Validation failed; inspect the errors object. |
429 | Rate limit exceeded; back off and retry after the interval. |
9. Rate Limits & Headers
| Header | Description |
|---|---|
Authorization | Bearer YOUR_TOKEN. Required for society and PMF endpoints. |
X-PMF-Token | Alternative to Authorization for PMF endpoints only. |
Content-Type | application/json for requests with a body. |
Accept | application/json. |
X-RateLimit-Limit | Maximum requests allowed in the current window. |
X-RateLimit-Remaining | Requests remaining in the current window. |
Retry-After | Seconds to wait after a 429 response. |
General API limit: 60 requests per minute per authenticated user (or per IP for public endpoints). Authentication endpoints are stricter: 10 requests per minute per IP for login, and 5 requests per minute per IP for registration and password reset.
Ready to build
Start your free trial and get API access immediately. Create PMF API tokens from the admin panel
Start Free Trial