SmartSpend API Documentation
PHP + MySQL backend for the SmartSpend family expense app.
Every endpoint below shows exactly what you send and what you get back.
Running on LIVE SERVER - connected to database "czsoft_smartspend". The API is ready to use.
Setup and base URL
This copy is running on the live server, so there is only one address.
It works from the browser, from Postman, from the emulator and from a real
phone anywhere in the world.
| Calling from | Base URL |
| Everywhere |
https://www.smartspend.creamerzsoft.com/ |
| Put this in the Android app |
https://www.smartspend.creamerzsoft.com/api/ |
Test accounts
| Role | Email | Password |
| Admin | admin@smartspend.com | admin123 |
| Member | ayesha@smartspend.com | member123 |
| Member | hamza@smartspend.com | member123 |
| Member | sara@smartspend.com | member123 |
Sending the token
Call Login first. It returns a token.
Save it in the app, then send it as a header on every endpoint marked
Login required or Admin only:
While testing you can also put it in the URL instead: ?token=d449b6b9...
Every response has the same shape
So in the app you always check success first, show
message to the user, and read the real content from
data.
When it works
{
"success": true,
"message": "Login successful",
"data": {
"...": "..."
}
}
When it fails
{
"success": false,
"message": "Incorrect email or password",
"errors": {
"email": "Invalid email"
}
}
Status codes used
| Code | Meaning |
| 200 | OK |
| 201 | Created - signup, add expense, add member |
| 400 | Bad request |
| 401 | Not logged in, or wrong password |
| 403 | Logged in, but not allowed (not the admin) |
| 404 | Not found |
| 405 | Wrong method - you sent GET to a POST endpoint |
| 409 | Email already registered |
| 422 | Validation failed - check the errors object |
| 500 | Server or database error |
Authentication
Start here. Login gives you a token - every other endpoint needs it.
POST
Sign up
No login needed
api/auth/signup.php
Creates a NEW household and makes this person its ADMIN. Use this on the Register screen. Other family members are added later by the admin using Add Member.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| name |
string |
required |
Full name of the person |
| email |
string |
required |
Must be a valid email and not already registered |
| password |
string |
required |
At least 6 characters |
| householdName |
string |
optional |
Family name. If you leave it out we use "<name>'s Family" |
| monthlyBudget |
number |
optional |
Budget for the whole family. Default 0 |
Example request body
{
"name": "Abdul Rehman",
"email": "abdul@example.com",
"password": "secret123",
"householdName": "Khan Family",
"monthlyBudget": 150000
}
What you get back
HTTP 201 · Content-Type: application/json
{
"success": true,
"message": "Account created successfully",
"data": {
"token": "7f3a9c2e1b4d...64 characters",
"user": {
"id": 1,
"householdId": 1,
"name": "Abdul Rehman",
"email": "abdul@example.com",
"role": "ADMIN",
"assignedBudget": 150000
}
}
}
If something goes wrong
| Code | message you get back |
| 422 |
Please fill all required fields |
| 422 |
Please enter a valid email address |
| 422 |
Password must be at least 6 characters |
| 409 |
This email is already registered |
POST
Login
No login needed
api/auth/login.php
Checks the email and password. Save the token you get back - you must send it on every protected request.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| email |
string |
required |
The registered email address |
| password |
string |
required |
The account password |
Example request body
{
"email": "admin@smartspend.com",
"password": "admin123"
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Login successful",
"data": {
"token": "d449b6b99cefda157444058487d862d6...",
"user": {
"id": 1,
"householdId": 1,
"name": "Abdul Rehman",
"email": "admin@smartspend.com",
"role": "ADMIN",
"assignedBudget": 50000
}
}
}
If something goes wrong
| Code | message you get back |
| 401 |
Incorrect email or password |
| 422 |
Please fill all required fields |
GET
Who am I
Login required
api/auth/me.php
Call this when the app opens to check the saved token is still valid. Also fills the profile screen.
What you send
Headers
Fields
Nothing to send - just the headers above.
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "User loaded",
"data": {
"user": {
"id": 1,
"householdId": 1,
"name": "Abdul Rehman",
"email": "admin@smartspend.com",
"role": "ADMIN",
"assignedBudget": 50000
},
"householdName": "Khan Family",
"totalSpent": 27700,
"remaining": 22300
}
}
If something goes wrong
| Code | message you get back |
| 401 |
Please login first (missing or expired token) |
POST
Forgot password
No login needed
api/auth/forgot_password.php
Creates a 6-digit code that works for 15 minutes. While DEBUG_MODE is true the code comes back in the response so you can test without an email server. The message is always the same whether the email exists or not, so nobody can use this to find out which emails are registered.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| email |
string |
required |
The email to send the reset code to |
Example request body
{
"email": "admin@smartspend.com"
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "If that email is registered, a reset code has been sent",
"data": {
"code": "874545",
"note": "Shown only because DEBUG_MODE is true"
}
}
If something goes wrong
| Code | message you get back |
| 422 |
Please enter a valid email address |
POST
Reset password
No login needed
api/auth/reset_password.php
Sets a new password using the 6-digit code. The code can only be used once. All old tokens are deleted, so the user must login again.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| email |
string |
required |
Same email used in Forgot password |
| code |
string |
required |
The 6-digit code |
| newPassword |
string |
required |
At least 6 characters |
Example request body
{
"email": "admin@smartspend.com",
"code": "874545",
"newPassword": "newpass123"
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Password changed successfully. Please login again.",
"data": null
}
If something goes wrong
| Code | message you get back |
| 400 |
This code is wrong or has expired |
| 422 |
Password must be at least 6 characters |
POST
Change password
Login required
api/auth/change_password.php
For the settings screen, when the user is already logged in and knows their current password.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| currentPassword |
string |
required |
The password they use right now |
| newPassword |
string |
required |
At least 6 characters |
Example request body
{
"currentPassword": "admin123",
"newPassword": "admin456"
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Password updated successfully",
"data": null
}
If something goes wrong
| Code | message you get back |
| 401 |
Your current password is incorrect |
| 422 |
New password must be at least 6 characters |
POST
Logout
Login required
api/auth/logout.php
Deletes the current token from the database so it can never be used again. The app should also delete its saved copy.
What you send
Headers
Fields
Nothing to send - just the headers above.
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Logged out successfully",
"data": null
}
If something goes wrong
| Code | message you get back |
| 401 |
Please login first (missing or expired token) |
Dashboard and Profile
One call fills the whole home screen - no need to call members and expenses separately.
GET
Dashboard
Login required
api/dashboard.php
Everything the home screen needs in ONE request: the logged-in user, the family budget, how much was spent this month, every member with their own numbers, the 5 newest expenses, spending grouped by category for the chart, and the unread count for the bell icon.
What you send
Headers
Fields
Nothing to send - just the headers above.
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Dashboard loaded",
"data": {
"user": {
"id": 1,
"householdId": 1,
"name": "Abdul Rehman",
"email": "admin@smartspend.com",
"role": "ADMIN",
"assignedBudget": 50000
},
"household": {
"id": 1,
"name": "Khan Family",
"monthlyBudget": 150000,
"totalSpent": 63850,
"remaining": 86150,
"usedPercent": 42.60000000000000142108547152020037174224853515625,
"expenseCount": 11,
"monthStart": "2026-08-01",
"monthLabel": "August 2026"
},
"mySpending": {
"assignedBudget": 50000,
"totalSpent": 27700,
"remaining": 22300
},
"members": [
{
"id": 1,
"name": "Abdul Rehman",
"email": "admin@smartspend.com",
"role": "ADMIN",
"assignedBudget": 50000,
"totalSpent": 27700,
"remaining": 22300,
"usedPercent": 55.39999999999999857891452847979962825775146484375,
"lastActive": "Just now"
},
{
"id": 2,
"name": "Ayesha Khan",
"email": "ayesha@smartspend.com",
"role": "MEMBER",
"assignedBudget": 30000,
"totalSpent": 17500,
"remaining": 12500,
"usedPercent": 58.2999999999999971578290569595992565155029296875,
"lastActive": "2 hours ago"
}
],
"recentExpenses": [
{
"id": 11,
"title": "Bus Card Top-up",
"amount": 1200,
"category": "Transport",
"date": "2026-08-12",
"memberId": 4,
"memberName": "Sara Khan"
}
],
"spendingByCategory": [
{
"category": "Groceries",
"amount": 21900,
"percent": 34.2999999999999971578290569595992565155029296875
},
{
"category": "Education",
"amount": 14750,
"percent": 23.10000000000000142108547152020037174224853515625
}
],
"unreadCount": 2
}
}
If something goes wrong
| Code | message you get back |
| 401 |
Please login first (missing or expired token) |
POST
Edit my profile
Login required
api/profile.php
Lets a user change their OWN name or email. Send only the field you want to change - anything you leave out keeps its old value.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| name |
string |
optional |
New display name |
| email |
string |
optional |
New email. Must not belong to someone else |
Example request body
{
"name": "Abdul Rehman Khan"
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Profile updated successfully",
"data": {
"user": {
"id": 1,
"householdId": 1,
"name": "Abdul Rehman Khan",
"email": "admin@smartspend.com",
"role": "ADMIN",
"assignedBudget": 50000
}
}
}
If something goes wrong
| Code | message you get back |
| 422 |
Please enter a valid email address |
| 409 |
This email is already used by someone else |
Expenses
By default every list shows only the CURRENT month. Add allMonths=1 to see everything.
GET
List expenses
Login required
api/expenses/list.php?category=Bills&page=1&limit=20
The main expense list. Every filter below is optional and they can be combined, for example ?memberId=3&category=Bills&page=2
What you send
Headers
Values added to the URL
| Field | Type | Required | What it means |
| memberId |
int |
optional |
Only this member's expenses |
| category |
string |
optional |
Groceries, Bills, Transport, Food, Health, Education, Shopping, Rent, Other |
| search |
string |
optional |
Search inside the expense title |
| from |
date |
optional |
Start date, format 2026-08-01 |
| to |
date |
optional |
End date, format 2026-08-31 |
| allMonths |
0 or 1 |
optional |
Send 1 to ignore the current-month filter |
| page |
int |
optional |
Page number. Default 1 |
| limit |
int |
optional |
Rows per page. Default 50, maximum 100 |
Example request
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Expenses loaded",
"data": {
"expenses": [
{
"id": 12,
"title": "Internet Bill",
"amount": 4500,
"category": "Bills",
"date": "2026-08-18",
"memberId": 1,
"memberName": "Abdul Rehman"
},
{
"id": 7,
"title": "Mobile Load",
"amount": 1500,
"category": "Bills",
"date": "2026-08-08",
"memberId": 3,
"memberName": "Hamza Khan"
}
],
"totalCount": 3,
"totalSpent": 15200,
"page": 1,
"limit": 20,
"totalPages": 1,
"categories": [
"Groceries",
"Bills",
"Transport",
"Food",
"Health",
"Education",
"Shopping",
"Rent",
"Other"
]
}
}
If something goes wrong
| Code | message you get back |
| 401 |
Please login first (missing or expired token) |
POST
Add expense
Login required
api/expenses/add.php
Saves a new expense. It also creates a notification for the family, and if the member goes past their assigned budget it creates a Budget Exceeded warning too. The response tells you overBudget so the app can show a message straight away.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| title |
string |
required |
What the money was spent on |
| amount |
number |
required |
Must be greater than 0 |
| category |
string |
optional |
One of the 9 categories. Anything else becomes "Other" |
| date |
date |
optional |
Format 2026-08-18. Defaults to today |
| memberId |
int |
optional |
Who the expense belongs to. Defaults to you. Only an ADMIN may put someone else's id here |
Example request body
{
"title": "Internet Bill",
"amount": 4500,
"category": "Bills",
"date": "2026-08-18"
}
What you get back
HTTP 201 · Content-Type: application/json
{
"success": true,
"message": "Expense added successfully",
"data": {
"expense": {
"id": 12,
"title": "Internet Bill",
"amount": 4500,
"category": "Bills",
"date": "2026-08-18",
"memberId": 1,
"memberName": "Abdul Rehman"
},
"memberSpent": 32200,
"memberBudget": 50000,
"overBudget": false
}
}
If something goes wrong
| Code | message you get back |
| 422 |
Please fill all required fields |
| 422 |
Amount must be greater than 0 |
| 422 |
Date must look like 2026-08-19 |
| 403 |
You can only add expenses for yourself |
| 404 |
That member is not part of your household |
POST
Edit expense
Login required
api/expenses/update.php
Changes an existing expense. Send only the fields you want to change. A normal member can only edit their own expenses; an ADMIN can edit any expense in the household.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| id |
int |
required |
Which expense to edit |
| title |
string |
optional |
New title |
| amount |
number |
optional |
New amount, greater than 0 |
| category |
string |
optional |
New category |
| date |
date |
optional |
New date, format 2026-08-18 |
Example request body
{
"id": 12,
"title": "Internet Bill (August)",
"amount": 5000
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Expense updated successfully",
"data": {
"expense": {
"id": 12,
"title": "Internet Bill (August)",
"amount": 5000,
"category": "Bills",
"date": "2026-08-18",
"memberId": 1,
"memberName": "Abdul Rehman"
}
}
}
If something goes wrong
| Code | message you get back |
| 404 |
Expense not found |
| 403 |
You can only edit your own expenses |
| 422 |
Amount must be greater than 0 |
POST
Delete expense
Login required
api/expenses/delete.php
Removes one expense. A normal member can only delete their own; an ADMIN can delete any.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| id |
int |
required |
Which expense to delete |
Example request body
{
"id": 12
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Expense deleted successfully",
"data": null
}
If something goes wrong
| Code | message you get back |
| 404 |
Expense not found |
| 403 |
You can only delete your own expenses |
GET
Category list
No login needed
api/expenses/categories.php
The 9 categories for the dropdown on the Add Expense screen. No login needed, so the app can load it early.
What you send
Headers
Fields
Nothing to send - just the headers above.
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Categories loaded",
"data": {
"categories": [
"Groceries",
"Bills",
"Transport",
"Food",
"Health",
"Education",
"Shopping",
"Rent",
"Other"
]
}
}
Family Members
Anyone logged in can VIEW members. Only the ADMIN can add, edit or remove them.
GET
List members
Login required
api/members/list.php
Every member of the household with their assigned budget, how much they spent this month, what is left, the percentage used and when they were last active. The admin is always first.
What you send
Headers
Fields
Nothing to send - just the headers above.
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Members loaded",
"data": {
"members": [
{
"id": 1,
"name": "Abdul Rehman",
"email": "admin@smartspend.com",
"role": "ADMIN",
"assignedBudget": 50000,
"totalSpent": 27700,
"remaining": 22300,
"usedPercent": 55.39999999999999857891452847979962825775146484375,
"lastActive": "Just now"
},
{
"id": 3,
"name": "Hamza Khan",
"email": "hamza@smartspend.com",
"role": "MEMBER",
"assignedBudget": 25000,
"totalSpent": 12800,
"remaining": 12200,
"usedPercent": 51.2000000000000028421709430404007434844970703125,
"lastActive": "1 day ago"
}
],
"totalMembers": 4
}
}
If something goes wrong
| Code | message you get back |
| 401 |
Please login first (missing or expired token) |
GET
Member detail
Login required
api/members/get.php?id=3
One member plus all of their expenses this month and their spending grouped by category. Use this for the Member Detail screen.
What you send
Headers
Values added to the URL
| Field | Type | Required | What it means |
| id |
int |
required |
Which member to load |
Example request
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Member loaded",
"data": {
"member": {
"id": 3,
"name": "Hamza Khan",
"email": "hamza@smartspend.com",
"role": "MEMBER",
"assignedBudget": 25000,
"totalSpent": 12800,
"remaining": 12200,
"usedPercent": 51.2000000000000028421709430404007434844970703125,
"lastActive": "1 day ago"
},
"expenses": [
{
"id": 8,
"title": "Cricket Kit",
"amount": 4800,
"category": "Shopping",
"date": "2026-08-11",
"memberId": 3,
"memberName": "Hamza Khan"
}
],
"expenseCount": 3,
"spendingByCategory": [
{
"category": "Transport",
"amount": 6500
},
{
"category": "Shopping",
"amount": 4800
},
{
"category": "Bills",
"amount": 1500
}
]
}
}
If something goes wrong
| Code | message you get back |
| 422 |
Please fill all required fields |
| 404 |
Member not found |
GET
Member expenses only
Login required
api/members/expenses.php?id=3
A lighter version of Member detail - just the expense list and the total. Useful for a "see all" screen.
What you send
Headers
Values added to the URL
| Field | Type | Required | What it means |
| id |
int |
required |
Which member |
| allMonths |
0 or 1 |
optional |
Send 1 to include older months as well |
Example request
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Member expenses loaded",
"data": {
"expenses": [
{
"id": 6,
"title": "Petrol",
"amount": 6500,
"category": "Transport",
"date": "2026-08-04",
"memberId": 3,
"memberName": "Hamza Khan"
}
],
"totalSpent": 12800,
"count": 3
}
}
If something goes wrong
| Code | message you get back |
| 404 |
Member not found |
POST
Add member
Admin only
api/members/add.php
The admin creates a login for a family member. The member can then login with the email and password given here.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| name |
string |
required |
Full name |
| email |
string |
required |
Must be valid and not already registered |
| password |
string |
required |
At least 6 characters. Give it to the member |
| assignedBudget |
number |
optional |
Their spending limit. Default 0 |
| role |
string |
optional |
MEMBER or ADMIN. Default MEMBER |
Example request body
{
"name": "Bilal Khan",
"email": "bilal@smartspend.com",
"password": "bilal123",
"assignedBudget": 15000,
"role": "MEMBER"
}
What you get back
HTTP 201 · Content-Type: application/json
{
"success": true,
"message": "Member added successfully",
"data": {
"member": {
"id": 5,
"name": "Bilal Khan",
"email": "bilal@smartspend.com",
"role": "MEMBER",
"assignedBudget": 15000,
"totalSpent": 0,
"remaining": 15000,
"usedPercent": 0,
"lastActive": "Never"
}
}
}
If something goes wrong
| Code | message you get back |
| 403 |
Only the household admin can do this |
| 422 |
Please fill all required fields |
| 422 |
Password must be at least 6 characters |
| 409 |
This email is already registered |
POST
Edit member
Admin only
api/members/update.php
Changes a member's name, email, role or budget. Send only what you want to change. The API refuses to remove the last ADMIN from a household.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| id |
int |
required |
Which member to edit |
| name |
string |
optional |
New name |
| email |
string |
optional |
New email |
| role |
string |
optional |
MEMBER or ADMIN |
| assignedBudget |
number |
optional |
New budget |
Example request body
{
"id": 2,
"name": "Ayesha K.",
"assignedBudget": 32000
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Member updated successfully",
"data": {
"member": {
"id": 2,
"name": "Ayesha K.",
"email": "ayesha@smartspend.com",
"role": "MEMBER",
"assignedBudget": 32000,
"totalSpent": 17500,
"remaining": 14500,
"usedPercent": 54.7000000000000028421709430404007434844970703125,
"lastActive": "2 hours ago"
}
}
}
If something goes wrong
| Code | message you get back |
| 403 |
Only the household admin can do this |
| 404 |
Member not found |
| 409 |
This email is already used by someone else |
| 400 |
The household must have at least one admin |
POST
Set member budget
Admin only
api/members/update_budget.php
The quick version of Edit member for the "Assign Budget" screen - it only changes the money. The response comes back with the member's fresh spending numbers so the screen can refresh immediately.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| id |
int |
required |
Which member |
| assignedBudget |
number |
required |
The new limit. Cannot be negative |
Example request body
{
"id": 3,
"assignedBudget": 26000
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Budget updated successfully",
"data": {
"member": {
"id": 3,
"name": "Hamza Khan",
"email": "hamza@smartspend.com",
"role": "MEMBER",
"assignedBudget": 26000,
"totalSpent": 12800,
"remaining": 13200,
"usedPercent": 49.2000000000000028421709430404007434844970703125,
"lastActive": "1 day ago"
}
}
}
If something goes wrong
| Code | message you get back |
| 403 |
Only the household admin can do this |
| 422 |
Budget cannot be negative |
| 404 |
Member not found |
POST
Remove member
Admin only
api/members/delete.php
Deletes the member AND all of their expenses and tokens. The admin cannot delete themselves.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| id |
int |
required |
Which member to remove |
Example request body
{
"id": 5
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Member removed successfully",
"data": null
}
If something goes wrong
| Code | message you get back |
| 403 |
Only the household admin can do this |
| 400 |
You cannot remove yourself |
| 404 |
Member not found |
Household
The household is the family itself - its name, its monthly budget and which month is running.
GET
Get household
Login required
api/household/get.php
The family name, monthly budget, how much is spent and left this month, and how many members there are.
What you send
Headers
Fields
Nothing to send - just the headers above.
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Household loaded",
"data": {
"household": {
"id": 1,
"name": "Khan Family",
"monthlyBudget": 150000,
"totalSpent": 63850,
"remaining": 86150,
"usedPercent": 42.60000000000000142108547152020037174224853515625,
"monthStart": "2026-08-01",
"monthLabel": "August 2026",
"memberCount": 4
}
}
}
If something goes wrong
| Code | message you get back |
| 401 |
Please login first (missing or expired token) |
POST
Edit household
Admin only
api/household/update.php
Changes the family name or the total monthly budget. Send only what you want to change.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| name |
string |
optional |
New family name |
| monthlyBudget |
number |
optional |
New total budget. Cannot be negative |
Example request body
{
"name": "Khan Family",
"monthlyBudget": 160000
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Household updated successfully",
"data": {
"household": {
"id": 1,
"name": "Khan Family",
"monthlyBudget": 160000
}
}
}
If something goes wrong
| Code | message you get back |
| 403 |
Only the household admin can do this |
| 422 |
Budget cannot be negative |
POST
Start new month
Admin only
api/household/new_month.php
The Start New Month button. It does NOT delete anything - old expenses stay in the database so the report screen still works. It only moves the month start date forward, so every screen begins counting from zero again. The response tells you what the closed month totalled, which is nice to show in a summary dialog.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| monthlyBudget |
number |
optional |
Set a different budget for the new month. Leave it out to keep the same one |
Example request body
{
"monthlyBudget": 160000
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "New month started successfully",
"data": {
"closedMonth": {
"from": "2026-08-01",
"to": "2026-09-01",
"totalSpent": 63850,
"expenseCount": 11
},
"newMonthStart": "2026-09-01",
"monthlyBudget": 160000
}
}
If something goes wrong
| Code | message you get back |
| 403 |
Only the household admin can do this |
GET
Reports
Login required
api/household/report.php?months=6
Totals for the reports and charts screen: month by month, then per member, then per category. Unlike the other endpoints this one looks at ALL months, not just the current one.
What you send
Headers
Values added to the URL
| Field | Type | Required | What it means |
| months |
int |
optional |
How many months of history. Default 6, maximum 24 |
Example request
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Report loaded",
"data": {
"monthly": [
{
"month": "2026-08",
"label": "August 2026",
"totalSpent": 63850,
"expenseCount": 11
},
{
"month": "2026-07",
"label": "July 2026",
"totalSpent": 58200,
"expenseCount": 9
}
],
"byMember": [
{
"memberId": 1,
"name": "Abdul Rehman",
"role": "ADMIN",
"totalSpent": 32700,
"expenseCount": 3
},
{
"memberId": 2,
"name": "Ayesha Khan",
"role": "MEMBER",
"totalSpent": 17500,
"expenseCount": 3
}
],
"byCategory": [
{
"category": "Groceries",
"totalSpent": 21900,
"expenseCount": 2
},
{
"category": "Bills",
"totalSpent": 15700,
"expenseCount": 3
}
]
}
}
If something goes wrong
| Code | message you get back |
| 401 |
Please login first (missing or expired token) |
Notifications
The API creates these by itself when an expense is added, a member joins, a budget is passed or a new month starts.
GET
List notifications
Login required
api/notifications/list.php?limit=20
Messages for this user plus the ones sent to the whole family. Newest first. type is INFO, EXPENSE_ADDED or BUDGET_WARNING so the app can show a different icon for each.
What you send
Headers
Values added to the URL
| Field | Type | Required | What it means |
| limit |
int |
optional |
How many to return. Default 50, maximum 100 |
Example request
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "Notifications loaded",
"data": {
"notifications": [
{
"id": 4,
"title": "New Expense Added",
"message": "Abdul Rehman added \"Internet Bill\" for Rs 4,500.00",
"type": "EXPENSE_ADDED",
"isRead": false,
"createdAt": "2026-08-19 01:52:14",
"timeAgo": "Just now"
},
{
"id": 2,
"title": "Budget Warning",
"message": "Hamza Khan has used more than 50% of the assigned budget.",
"type": "BUDGET_WARNING",
"isRead": true,
"createdAt": "2026-08-19 01:29:03",
"timeAgo": "23 mins ago"
}
],
"unreadCount": 1
}
}
If something goes wrong
| Code | message you get back |
| 401 |
Please login first (missing or expired token) |
POST
Mark as read
Login required
api/notifications/mark_read.php
Send id to mark ONE notification as read, or send all=1 to clear the whole list. Use all=1 behind a "Mark all as read" button.
What you send
Headers
Fields in the JSON body
| Field | Type | Required | What it means |
| id |
int |
optional |
Which notification. Required unless you send all |
| all |
0 or 1 |
optional |
Send 1 to mark every notification as read |
Example request body
{
"all": 1
}
What you get back
HTTP 200 · Content-Type: application/json
{
"success": true,
"message": "All notifications marked as read",
"data": null
}
If something goes wrong
| Code | message you get back |
| 422 |
Please fill all required fields |
| 404 |
Notification not found |
Debug mode is OFF.
Because this is the live server, database errors are hidden and
Forgot password no longer returns the code in the response.