GramPanchayat API Documentation
Node.js + Express + MongoDB backend powering the GramPanchayat citizen app, the GP Admin panel and the Super Admin panel. All endpoints are prefixed with /api and return JSON in the shape { success, message, data }.
Authorization: Bearer <accessToken>. Get a token pair via
POST /api/auth/request-otp → POST /api/auth/verify-otp.
Access tokens expire in 15 minutes by default — use
POST /api/auth/refresh-token to get a new one without logging in again.
Roles. Every user has one role. Three are login/permission roles:
citizen, gp_admin, super_admin.
Citizens and gp_admins are scoped to a single Gram Panchayat
(user.gp); super_admin can see across all Gram Panchayats.
The remaining roles are static GP-staff designations a
gp_admin or super_admin can tag onto a user purely for record-keeping —
they carry no extra API access of their own; a user tagged
talathi still uses the app as a citizen would.
See src/utils/roles.js for the canonical list.
| Role | Can do |
|---|---|
citizen | Use all citizen-facing services: complaints, certificates, tax, schemes, jobs, marketplace, social feed — scoped to their own Gram Panchayat. |
gp_admin | Everything a citizen can do, plus manage/approve/resolve requests, publish notices, manage users, tax records, development works, etc. — scoped to their own Gram Panchayat. |
super_admin | Onboard/manage Gram Panchayats, create gp_admin accounts, view platform-wide analytics and audit logs, broadcast notifications. |
lightman | GP-staff designation (लाईटमन). Assignable by gp_admin or super_admin. |
water_tax_collector | GP-staff designation (पाणी कर संग्राहक). Assignable by gp_admin or super_admin. |
gram_sevak | GP-staff designation (ग्रामसेवक). Assignable by gp_admin or super_admin. |
talathi | GP-staff designation (तलाठी). Assignable by gp_admin or super_admin. |
arogya_sevika | GP-staff designation (आरोग्य सेविका). Assignable by gp_admin or super_admin. |
anganwadi_sevika | GP-staff designation (अंगणवाडी सेविका). Assignable by gp_admin or super_admin. |
news | GP-staff designation (न्यूज / वार्ताहर). Super-admin only — not assignable from the GP Admin panel. |
kotwal | GP-staff designation (कोतवाल). Super-admin only — not assignable from the GP Admin panel. |
Pagination. Most list (
GET) endpoints share the same
paginate() helper (src/utils/helpers.js) and accept
?page= (default 1) and ?limit= (default
20, max 100), returning
{ items, total, page, limit }. The client compares
page * limit against total to know whether there's a
next page. This is what the app uses for infinite-scroll lists (e.g.
VillageEventsScreen, WaterSupplyScreen, AgriVegPriceScreen,
AnimalBuyScreen, SchemeInfoScreen, KaradNewsScreen,
ImportantContactsScreen — both the citizen read view
at GET /api/villages/:id/contacts and the gp_admin
management view at GET /api/gp-admin/contacts) instead of
loading an entire GP's data — or, for feeds like न्यूज़/जनावरे बाजार, an
entire town's data — in one call: only the first page loads up front
(with a skeleton placeholder while it does), and later pages are
fetched quietly as the user scrolls near the bottom.
TaxPaymentScreen (citizen) is a related but slightly
different case: GET /api/tax/summary/mine is scoped to one
citizen's own records and stays a single call, but its "मागील व्यवहार"
payment-history list is now revealed client-side in the same
batch-at-a-time way (see the screen's _historyVisibleCount)
so a citizen with years of payments doesn't get every row built at
once. GET /api/tax (used by gp_admin's तक्ता/list views) was
already paginated.
Auth
Mobile number + OTP login used by the login/OTP screens. Works identically for citizens, gp_admin and super_admin — role is stored server-side against the mobile number.
Sends a 6-digit OTP to the given mobile number (creates no user yet).
Body
{ "mobile": "9876543210" }
Response
{ "success": true, "data": { "mobile": "9876543210", "expiresInMinutes": 5, "devOtp": "123456" } }
devOtp is only returned when OTP_DEV_MODE=true in .env, so you can test without an SMS gateway.
Verifies the OTP and returns access + refresh tokens. Creates the user (role=citizen) on first login.
Body
{ "mobile": "9876543210", "otp": "123456" }
Response
{
"success": true,
"data": {
"accessToken": "...",
"refreshToken": "...",
"isNewUser": true,
"user": { "id": "...", "mobile": "9876543210", "role": "citizen", "isProfileComplete": false }
}
}
Exchange a refresh token for a fresh access token.
Body
{ "refreshToken": "..." }
Returns the currently authenticated user.
Stateless logout — client discards tokens locally.
- Run
npm run seedonce — creates/promotes the user atSUPER_ADMIN_MOBILE(default9999999999) to rolesuper_admin. POST /api/auth/request-otpwith{ "mobile": "9999999999" }. WithOTP_DEV_MODE=truethe response includes"devOtp": "123456"— no SMS is actually sent.POST /api/auth/verify-otpwith{ "mobile": "9999999999", "otp": "123456" }→ response hasaccessTokenanduser.role: "super_admin".- Send that token as
Authorization: Bearer <accessToken>on every/api/super-admin/*and/api/locations/importcall below.
Same flow for a gp_admin — either seeded via POST /api/super-admin/gps with an adminMobile, or promoted from a citizen via PATCH /api/gp-admin/users/:id/role (see GP Admin Panel below) — logs in through the exact same request-otp / verify-otp pair using their own mobile number.
Citizen Profile
Maps to the नागरिक माहिती (citizen info) form.
Get the logged-in user's full profile.
Create/update citizen profile (नागरिक माहिती form). For citizens, if district/taluka/village are all sent and no explicit gpId is given, the server looks up an active Gram Panchayat matching that exact combination and auto-links it.
Body
{
"fullName": "Ramesh Patil",
"gender": "male",
"dob": "1990-05-12",
"district": "पुणे",
"taluka": "भोर",
"village": "देवागाव",
"ward": "3",
"profileImage": "https://.../uploads/xyz.jpg",
"gpId": "665f1..."
}
If no active Gram Panchayat is registered for the given district/taluka/village (and no gpId was sent), the profile is not saved and the API responds 404 with:
{
"success": false,
"message": "तुमच्या गावासाठी ग्रामपंचायत अद्याप उपलब्ध नाही. कृपया ग्रामपंचायत कार्यालयाशी संपर्क साधा."
}
Locations (District / Taluka / Village)
Cascading master list that powers the नागरिक माहिती form's जिल्हा → तालुका → गाव dropdowns. Reads are public; writes/imports require a super_admin Bearer token (see the Super Admin quick-start box in the Auth section above).
Sorted list of all districts.
Query: district (required)
Query: district, taluka (both required)
Add a single district/taluka/village row.
Body
{ "district": "पुणे", "taluka": "भोर", "village": "देवागाव" }Bulk import — the easy way to load an official list in one call. Duplicates are skipped automatically.
Body
{ "items": [
{ "district": "पुणे", "taluka": "भोर", "village": "देवागाव" },
{ "district": "पुणे", "taluka": "हवेली", "village": "वडगाव" }
] }
Example (curl, with token)
curl -X POST https://your-domain/api/locations/import \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{ "items": [ { "district": "पुणे", "taluka": "भोर", "village": "देवागाव" } ] }'
Same result can be achieved offline via node src/utils/importLocations.js path/to/data.json (also accepts .csv, no token needed — it talks to MongoDB directly) — see src/utils/sample-locations.json for the expected shape.
Deactivate (soft-delete) one row.
Villages / Gram Panchayat Info
Village profile, sarpanch info, GP members — public read, managed by super_admin.
List/search Gram Panchayats for the citizen onboarding "select your village" step.
Query: search, district, taluka, page, limit
Checks whether an active Gram Panchayat exists for an exact district+taluka+village combination — used by नागरिक माहिती registration right before saving, for an instant Marathi "not available" message instead of waiting on the profile save to fail.
Query: district, taluka, village (all required)
Response
{ "found": true, "gp": { "_id": "665f1...", "nameMarathi": "देवागाव ग्रामपंचायत", ... } }Village profile screen: sarpanch info, about, contact, cover image.
GP staff (gp_admin users) tied to this Gram Panchayat.
महत्वाचे संपर्क (important_contacts_screen.dart, citizen side) - read-only list of the GP's important contacts. Contacts are added/edited/removed by gp_admin via /api/gp-admin/contacts. Paginated via paginate() — response is { items, total, page, limit }; ImportantContactsScreen loads the next page as the citizen scrolls near the bottom instead of downloading the whole contact book.
Query: category (optional — e.g. government, police, medical, other), page, limit
आमच्याबद्दल (VillageProfileScreen, citizen side) - read-only, type-wise list of a GP's facility info (शाळा, आरोग्य केंद्र, रस्ते, स्मशानभूमी, मंदिरे, शासकीय इमारती व ग्रामपंचायत कार्यालये, इतर) — each entry has a title, info and optional photo. Entries are added/edited/removed by gp_admin via /api/gp-admin/about-info.
Query: category (optional — one of the categories above)
गौरवशाली व्यक्ती (VillageProfileScreen, citizen side) - read-only list of notable persons of the GP (माजी सैनिक, शिक्षक, सरपंच, इतर) — type, name, info, photo. Entries are added/edited/removed by gp_admin via /api/gp-admin/notable-persons.
Query: type (optional — माजी सैनिक, शिक्षक, सरपंच, इतर)
पर्यटन स्थळे (VillageProfileScreen, citizen side) - read-only, flat list of the GP's tourist/scenic places — name, description, photo. Entries are added/edited/removed by gp_admin via /api/gp-admin/tourist-places.
ग्राम बॉडी (VillageProfileScreen, citizen side) - read-only list of the GP's body members — type (सरपंच, उपसरपंच, सदस्य, ग्रामसेवक, कोषाध्यक्ष, इतर), name, contact, photo. Entries are added/edited/removed by gp_admin via /api/gp-admin/gram-body.
Query: type (optional — one of the types above)
Complaints
Water / light / road / electricity / sanitation complaints, with status tracking.
Submit a complaint.
Body
{ "type": "water", "title": "No water supply", "description": "...", "images": ["url1"], "location": "Ward 4", "ward": "4" }List complaints. Citizens see only their own; gp_admin/super_admin pass mine=false (or omit mine) to see every complaint filed in their own ग्रामपंचायत — used by ResolveComplaintsScreen ("तक्रारी निकाली काढा"). Each item's user field is populated with { fullName, mobile } so the admin list can show the citizen's name/ward without a second lookup.
Query: type, status, mine, page, limit
Single complaint details.
Edit a complaint's own fields — type/title/description/images/location/ward. Backs the "संपादित करा" (edit) action on पाणी/रस्ता/दिवाबत्ती तक्रार screens and तक्रार स्थिती. A citizen may only edit their own complaint while it is still pending; once the GP office marks it in_progress/resolved/rejected only gp_admin/super_admin can edit it further.
Body
{ "type": "water", "title": "पाइप फुटला", "description": "...", "images": ["url1"], "location": "Ward 3", "ward": "3" }Update status, resolution note/photos, assignee, priority. Backs the "पूर्ण करा" (mark resolved) action on ResolveComplaintsScreen.
Body
{ "status": "resolved", "resolutionNote": "Fixed the pipeline leak.", "resolutionImages": [] }Append a dated progress note to a complaint's internal timeline. Backs the "प्रक्रियेत घ्या" / "नोंद जोडा" dialog on ResolveComplaintsScreen. Set changeStatus: true to also flip a still-pending complaint to in_progress in the same call (used for "प्रक्रियेत घ्या"; omit/false for a plain "नोंद जोडा" that leaves status untouched). Returns the full updated complaint including logs[].
Body
{ "note": "तांत्रिक टीम पाहणीसाठी पाठवली", "date": "2026-08-29", "changeStatus": true }Withdraw / delete a complaint. Citizens can withdraw any of their own complaints (any status); gp_admin/super_admin can delete any.
Storage note: all four तक्रार types (पाणी/रस्ता/दिवाबत्ती + वीज) share a single complaints MongoDB collection, distinguished by the type field (water | light | road | electricity | sanitation | other) — one API, one table, filterable per screen via ?type=. तक्रार स्थिती (ComplaintStatusScreen) simply calls GET /api/complaints?mine=true with no type filter to show every complaint the citizen has filed, across all types, in one list.
Certificates
Birth, death, marriage, residence, BPL, Niradhar-age, no-dues and generic certificate applications (दाखले).
Apply for a certificate.
Body
{
"type": "birth",
"applicantDetails": { "childName": "...", "dob": "2024-01-01", "fatherName": "...", "motherName": "..." },
"documents": [{ "name": "hospital_slip", "url": "https://.../uploads/x.pdf" }]
}Query: type, status, mine, page, limit
Application status/details (certificate_status_screen).
Review, approve/reject, or issue with a certificate number & PDF.
Body
{ "status": "issued", "certificateNumber": "GP/2026/0123", "certificateFile": "https://.../cert.pdf", "fee": 50 }Correct the type-specific form fields after submission (typo fix, missing field, etc). Only updates that certificate's own row in its type-specific table (birthcertificatedetails, deathcertificatedetails, ...) — nothing else on the base certificates row changes.
Body
{ "applicantDetails": { "childName": "Corrected Name" } }
Response shape is identical to the other certificate endpoints — a certificate object with a flat applicantDetails.
Storage note: each certificate type is stored in its own MongoDB collection (e.g. birthcertificatedetails, marriagecertificatedetails, ...) linked back to the base certificates collection via a details reference. The request/response JSON shape above is unchanged — this is an internal storage detail only.
Tax Payments
House tax and water tax records, itemised घरपट्टी / दिवाबत्ती कर / आरोग्य कर breakdown, dues and payment/receipt history. Powers tax_payment_screen.dart (citizen) and gp_admin/screens/tax_payments_screen.dart (admin). GET /api/tax is server-paginated (page/limit); GET /api/tax/summary/mine stays a single call scoped to one citizen, whose "मागील व्यवहार" history the citizen screen now reveals a batch at a time client-side as it scrolls, instead of building every row up front.
Create a tax demand for a citizen. Pass either a flat amountDue, or an itemised breakdown and the total is computed automatically.
Body
{ "user": "665f...", "type": "house", "propertyId": "1001", "ownerName": "...", "occupantName": "...",
"financialYear": "2025-26",
"breakdown": { "houseTax": 340, "streetLightTax": 10, "healthTax": 10 },
"dueDate": "2026-03-31" }Query: type, status, mine, page, limit
All of the logged-in citizen's own tax records grouped by propertyId - powers the "मालमत्ता निवडा" property picker and "मागील व्यवहार" payment history in one call.
Tax status / house / water tax detail, including breakdown and paymentHistory (each entry now carries a receiptNo).
Record a payment (online "Pay Now" by citizen, or offline cash entry by gp_admin). Auto-generates a "कराची पावती" receiptNo.
Body
{ "amount": 360, "mode": "upi", "transactionId": "T123456" }Edit a demand (propertyId, owner/occupant names, financialYear, breakdown, dueDate, status).
Remove a wrongly-entered demand.
Bulk-import rows straight from the .xlsx sheet used in tax_payments_screen.dart. Each row resolves its citizen by mobile (auto-creating one if needed) and upserts a 'house' record (+ a 'water' record when panipatti/waterTax is present).
Body
{ "financialYear": "2025-26", "rows": [
{ "mobile": "9876543210", "ownerName": "रमेश पाटील", "propertyId": "1",
"houseTax": 800, "streetLightTax": 100, "healthTax": 100, "arrears": 0, "panipatti": 200 }
] }Notices
Announcements, government orders, gram sabha notices, tax notices.
Body
{ "type": "gram_sabha", "titleMarathi": "ग्रामसभा सूचना", "description": "...", "eventDate": "2026-09-01" }Query: type (announcement/order/gram_sabha/tax), page, limit
Gov. Notice (जाहीरनामा / शासन आदेश / कर नोटीस)
Dedicated collection for government notices shown behind the citizen "Gov. Notice" button on gp_user_home_screen.dart (opens NoticeDashboardScreen) — deliberately a standalone API, not /api/notices. One schema, one set of CRUD routes, and a required type field split three ways:
| type | Marathi | Citizen screen | gp_admin screen |
|---|---|---|---|
jahirnama | जाहीरनामा | notice_announcements_screen.dart | gp_admin/screens/gov_notice_jahirnama_screen.dart |
shasan_adesh | शासन आदेश | notice_orders_screen.dart | gp_admin/screens/gov_notice_shasan_adesh_screen.dart |
kar_notice | कर नोटीस | notice_tax_screen.dart | gp_admin/screens/gov_notice_kar_notice_screen.dart |
type and title are required. Created under the caller's own GP (gp is taken from the session, not the request body, unless called by super_admin). To attach a PDF/image, first POST it to /api/uploads and put the returned url in attachment.
Body
{
"type": "jahirnama",
"title": "मनरेगा अंतर्गत कामगार नोंदणी सुरू",
"category": "योजना",
"department": "ग्रामपंचायत देवागाव",
"noticeNo": "GP/2026/JN/014",
"summary": "मनरेगा अंतर्गत नवीन कामगार नोंदणी सुरू.",
"description": "महात्मा गांधी रोजगार हमी योजनेअंतर्गत नवीन कामगार नोंदणी सुरू झाली आहे...",
"attachment": "https://gavapp.com/uploads/1732000000-notice.pdf",
"tags": ["मनरेगा", "रोजगार"],
"noticeDate": "2026-09-01",
"deadlineDate": "2026-09-30",
"contactName": "ग्रामसेवक",
"contactPhone": "9876543210",
"isPinned": true
}
type: jahirnama | shasan_adesh | kar_notice (required) · effectiveDate is mainly used for shasan_adesh; deadlineDate mainly for jahirnama/kar_notice — both accepted regardless of type.
Lists Gov. Notices for the caller's own GP — pinned first, then most recent noticeDate.
Query: type (jahirnama/shasan_adesh/kar_notice), category, page, limit
Edit any field from the create body above — pass only what changed.
GovNotice document fields
| Field | Type | Notes |
|---|---|---|
gp | ObjectId | Owning Gram Panchayat (auto, from session) |
type | String enum | Required — jahirnama | shasan_adesh | kar_notice |
title | String | Required — शीर्षक |
category | String | Free-text; meaning depends on type (see admin form dropdowns) |
department | String | जारी करणारा विभाग/कार्यालय |
noticeNo | String | सूचना/आदेश क्रमांक |
summary | String | छोटा सारांश (कार्ड प्रीव्ह्यू) |
description | String | सविस्तर मजकूर |
attachment | String | PDF/image URL from /api/uploads |
tags | String[] | Free-text tags |
noticeDate | Date | जाहीर/सूचना दिनांक (डीफॉल्ट: आत्ताची वेळ) |
effectiveDate | Date | प्रभावी दिनांक (मुख्यतः शासन आदेश) |
deadlineDate | Date | मुदत/देय तारीख (मुख्यतः जाहीरनामा/कर नोटीस) |
contactName / contactPhone | String | संपर्क तपशील |
isPinned | Boolean | डीफॉल्ट false — pinned notices sort first |
isActive | Boolean | डीफॉल्ट true — false = कालबाह्य/रद्द |
publishedBy | ObjectId | User who created it (auto, from session) |
createdAt / updatedAt | Date | Auto (Mongoose timestamps) |
Gram Sabha (ग्रामसभा)
Dedicated collection for ग्रामसभा sittings — separate from the generic Notices module. gp_admin publishes/edits/removes gram sabha notices (title, venue, date+time, agenda, notice number, attendee summary, status); citizens read them scoped to their own Gram Panchayat. Backs gp_admin/screens/gramsabha_screen.dart (Add/Edit/Delete) and screens/notice_gram_sabha_screen.dart (citizen list).
title, venue and meetingDate are required. Created under the caller's own GP (gp is taken from the session, not the request body, unless called by super_admin).
Body
{
"title": "विशेष ग्रामसभा - जुलै २०२५",
"category": "विशेष सभा",
"venue": "ग्रामपंचायत सभागृह, देवागाव",
"meetingDate": "2026-09-15T05:30:00.000Z",
"noticeNo": "GP/GS/2025/07/01",
"agenda": [
"प्रधानमंत्री आवास योजना लाभार्थी यादी मंजुरी",
"गावातील रस्ते दुरुस्तीसाठी निधी मंजुरी",
"इतर विषय"
],
"attendees": "१८ सदस्य",
"description": "सर्व ग्रामस्थांना कळविण्यात येते की...",
"status": "upcoming"
}
category: फक्त सूचनात्मक फ्री-टेक्स्ट, उदा. सर्वसाधारण सभा | विशेष सभा | महिला सभा | आपत्कालीन सभा | इतर · status: upcoming | held | cancelled (डीफॉल्ट upcoming)
Lists gram sabha notices for the caller's own GP, most recent meeting date first.
Query: status (upcoming/held/cancelled), page, limit
Edit any field from the create body above — pass only what changed.
GramSabha document fields
| Field | Type | Notes |
|---|---|---|
gp | ObjectId | Owning Gram Panchayat (auto, from session) |
title | String | Required — सभेचे शीर्षक/विषय |
category | String | सभेचा प्रकार |
venue | String | Required — सभेचे ठिकाण |
meetingDate | Date | Required — तारीख + वेळ एकत्र (ISO-8601) |
noticeNo | String | सूचना क्रमांक |
agenda | String[] | अजेंडा विषयांची यादी |
attendees | String | उपस्थितीचा तपशील |
description | String | सभेचे वर्णन |
status | String enum | upcoming | held | cancelled |
publishedBy | ObjectId | User who created it (auto, from session) |
createdAt / updatedAt | Date | Auto (Mongoose timestamps) |
Development Works
Village development works — ongoing/completed/planned, with budget, contractor and photos.
Query: status (ongoing/completed/planned)
Village Events (गावातील कार्यक्रम)
Community events and programs posted by citizens for their own Gram Panchayat — weddings, bhajans, sports, gram sabha reminders, etc. Unlike Notices/Development Works, any logged-in citizen can post one, not just gp_admin.
Creates an event under the caller's own GP (gp and createdBy are taken from the session, not the request body).
Body
{ "title": "हनुमान जयंती उत्सव", "category": "धार्मिक", "eventDate": "2026-09-14T07:00:00.000Z", "eventTime": "सकाळी 7:00", "place": "हनुमान मंदिर, तांबवे", "organizer": "मंदिर ट्रस्ट कमिटी", "organizerRole": "मंदिर समिती", "description": "...", "photo": "https://gavapp.com/uploads/xyz.jpg" }Lists events for the caller's own GP, soonest first.
Query: category (सांस्कृतिक/क्रीडा/धार्मिक/शासकीय/शैक्षणिक/वैयक्तिक कार्यक्रम), upcoming (true = today onward only), page (default 1), limit (default 20, max 100). Response: { items, total, page, limit } — total tells the client when to stop paging (infinite scroll on VillageEventsScreen).
Only the citizen who posted the event, or a gp_admin/super_admin, may edit it — otherwise 403.
Same ownership rule as PUT.
Schemes (सरकारी योजना)
Government/GP schemes: info, eligibility check, apply, document upload, application review. Powers SchemeInfoScreen (list + detail), SchemeEligibilityScreen, SchemeDocumentsScreen, SchemeApplyScreen.
Body
{
"title": "प्रधानमंत्री आवास योजना",
"nameEnglish": "PM Awas Yojana (PMAY)",
"shortDesc": "गरीब कुटुंबांना परवडणारे घर मिळण्यासाठी आर्थिक मदत.",
"ministry": "गृहनिर्माण व शहरी व्यवहार मंत्रालय",
"category": "आवास",
"benefits": "₹1.20 ते ₹2.50 लाख अनुदान",
"eligibility": "...", "targetGroup": "...", "launchYear": "2015",
"officialLink": "https://pmaymis.gov.in",
"highlights": ["...", "..."],
"documentsRequired": ["आधार कार्ड", "उत्पन्नाचा दाखला"],
"iconKey": "home", "colorHex": "#1565C0"
}
iconKey/colorHex drive the client's icon and color (Mongo can't store a Flutter IconData) — see SchemeModel._iconByKey in scheme_info_screen.dart for the supported keys.
Active schemes (GP-specific + global). Ships with 2 demo records via npm run seed:schemes — see src/utils/seedSchemes.js.
Query: page (default 1), limit (default 20, max 100). Response: { items, total, page, limit } — SchemeInfoScreen pages this (infinite scroll) rather than fetching every published scheme up front.
Edit a scheme. A gp_admin may only edit a scheme that belongs to their own GP, or a global (gp: null) scheme they created themselves — otherwise 403. super_admin can edit any scheme, including reassigning gp. Body accepts any subset of the fields shown in the POST example above. Once saved, the change is visible to every other user the next time they call GET /api/schemes / GET /api/schemes/:id (e.g. pull-to-refresh on SchemeInfoScreen).
Delete a scheme. Same ownership rule as PUT. The scheme disappears from every user's list immediately.
scheme_eligibility_screen data.
Body
{ "documents": [{ "name": "income_cert", "url": "https://.../doc.pdf" }] }Query: status
Body
{ "status": "approved", "remarks": "Verified documents" }Karad News (कराड न्यूज)
Taluka-wise citizen news feed — every post belongs to a taluka, so the feed can be filtered per-taluka instead of one fixed city-wide list. Any logged-in citizen can post; only the poster (or a gp_admin/super_admin) can edit or delete their own post — everyone else can only view and like/share. Powers KaradNewsScreen, KaradNewsDetailScreen and KaradNewsAddScreen.
Body
{
"title": "कराड शहरात नवीन उड्डाणपुलाचे भूमिपूजन संपन्न",
"category": "ब्रेकिंग",
"taluka": "कराड",
"channel": "कराड टुडे न्यूज",
"channelVerified": true,
"caption": "आज सकाळी...",
"mediaUrl": "https://.../uploads/xyz.jpg",
"mediaType": "image"
}
category is one of ब्रेकिंग / राजकारण / क्रीडा / हवामान / सामाजिक. taluka is optional in the request body — if omitted, it defaults to the posting user's own taluka (from their profile); a 400 is returned if neither is available. Upload the photo/video separately via POST /api/uploads first, then pass the returned URL as mediaUrl. Publishing is restricted to FEATURE_ROLES.karadNews = ['news', 'gp_admin', 'super_admin'] — a plain citizen gets a 403.
Query: category, taluka, mine=true (only the caller's own posts), page (default 1), limit (default 20, max 100). Response: { items, total, page, limit } — KaradNewsScreen pages this per selected taluka (infinite scroll) rather than fetching the whole feed up front.
taluka is taluka-wise: omit it to default to the caller's own taluka, pass taluka=सर्व to see every taluka's news with no filter, or pass a specific taluka name to see only that one's feed.
Each item includes likeCount and isLiked (whether the calling user has liked it) computed server-side. The response also echoes appliedTaluka — the taluka filter that was actually applied (null when none was).
Distinct, sorted list of talukas that currently have at least one news item, for populating the taluka filter chips on KaradNewsScreen. The caller's own taluka (myTaluka) is always included even if it has no posts yet, so their chip never disappears. Response: { "talukas": ["कराड", "वाई", ...], "myTaluka": "कराड" }.
Note: this route must stay registered above GET /api/karad-news/:id in karadNews.routes.js, or Express will treat talukas as an :id value instead.
Full details of a single news item — backs KaradNewsDetailScreen (opened by tapping a card). Also increments the view counter.
gp_admin/super_admin can edit any post. Otherwise: only the original poster, and only if they still hold the news role — a citizen who posted news before this role gate existed can no longer edit it unless promoted to news/gp_admin/super_admin. Anyone else gets a 403 with a Marathi explanation. Accepts the same fields as POST, including taluka — any field omitted from the body is left unchanged.
Same owner+role rule as PUT above.
Toggles like. Response: { "liked": true, "likeCount": 42 }
Bumps the share counter.
Job Portal
Citizen job board — post (JobCreateScreen), approve (gp_admin moderation), browse (JobListScreen), view (JobDetailScreen), apply (JobApplyScreen).
Post a job. Citizen posts go to approvalStatus: pending until a gp_admin approves them; gp_admin/super_admin posts are auto-approved. title, company and location are required.
Body
{
"title": "शेती सहाय्यक",
"company": "कृषी विभाग महाराष्ट्र",
"location": "देवागाव",
"salaryRange": "₹18,000 - ₹22,000",
"category": "agriculture",
"jobType": "full_time",
"qualification": "12वी उत्तीर्ण",
"description": "...",
"requirements": "...",
"contactNumber": "9876543210",
"vacancies": 2
}
category: govt | private | agriculture | construction | education | health | it · jobType: full_time | part_time | contract | temporary
List jobs for JobListScreen. Citizens/general users see only approvalStatus: approved + status: open jobs in their own ग्रामपंचायत; gp_admin/super_admin also see pending/rejected (filterable via status). Pass mine=true to fetch only jobs you posted, in any approval/open status — this backs a "माझ्या जाहिराती" view so a citizen's own pending post doesn't just disappear after submitting.
Query: category, jobType, location (contains match), q (search title/company/location), status (admin only), mine, page, limit
A citizen's own submitted applications, most recent first, each with the parent job populated (title, company, location, salaryRange). Declared before GET /:id in the router so applications is never parsed as a job id.
Single job details for JobDetailScreen.
Backs ApproveJobsScreen's मंजूर/नाकारा actions.
Body
{ "approvalStatus": "approved" }Edit any posted field (title, company, description, category, jobType, salaryRange, location, requirements, contactNumber, qualification, vacancies, status). When the owner (not gp_admin/super_admin) edits an already-approved job it is reset to approvalStatus: pending for re-review, same pattern as complaint re-flagging on edit.
Deletes the job and cascades to remove its JobApplication records.
Submit an application from JobApplyScreen. Rejected with a 400 if the job isn't currently approved + open, or if the citizen already has an applied/shortlisted application on this job. applicantName and a 10-digit applicantMobile are required — the rest is optional.
Body
{
"applicantName": "अमोल पाटील",
"applicantMobile": "9876543210",
"applicantEmail": "amol@example.com",
"applicantAddress": "देवागाव, ता. हवेली, जि. पुणे",
"qualification": "पदवीधर (Graduate)",
"experience": "2-3 वर्षे",
"resume": "https://.../resume.pdf",
"coverNote": "..."
}List every applicant for a job you posted (or any job, as gp_admin/super_admin). Each item's user field is populated with { fullName, mobile } alongside the applicant details captured on JobApplyScreen (applicantName, applicantMobile, applicantEmail, applicantAddress, qualification, experience).
Marketplace
Business, farming and general buy/sell listings.
Body
{ "category": "farming", "title": "Fresh onions", "price": 25, "unit": "per kg", "images": [] }Query: category, status, mine, page (default 1), limit (default 20, max 100). Response: { items, total, page, limit } — AnimalBuyScreen's शेतमाल/इतर tabs page this (infinite scroll) rather than fetching everything up front.
Animal Marketplace
Buy/sell livestock listings (animal_buy_screen, animal_sell_screen, animal_marketplace_screen).
Body
{ "animalType": "cow", "breed": "Gir", "age": "3 years", "price": 45000, "milkCapacity": "10L/day", "vet": "2" }
vet — वेत (calving/lactation count), e.g. "2" or "2 वेत". Optional; applies to milking animals (cow/buffalo/goat/sheep), omitted for bull/other.
Query: animalType, status, mine, vet, page (default 1), limit (default 20, max 100). Response: { items, total, page, limit } — AnimalBuyScreen pages this per category tab (infinite scroll) rather than fetching the whole marketplace up front.
vet filters listings by exact वेत value, e.g. ?vet=2.
Local Business (स्थानिक व्यवसाय)
Village business directory — MarketplaceBusinessScreen
(lib/screens/marketplace_business_screen.dart). This is a standalone
collection, deliberately separate from /api/marketplace
(which is priced buy/sell listings): a business here has no price,
just an owner, contact details, category and an उघडे/बंद status. Any
citizen can add a business; only its creator can edit, delete or
toggle its open/closed status. Category must be one of
दुकान, सेवा, कारागीर,
हॉटेल, वाहतूक, इतर — matching
the screen's filter chips exactly.
Register a new business. owner (the DB user) and gp are set automatically from the logged-in user.
Body
{
"name": "पाटील किराणा स्टोअर",
"nameEn": "Patil General Store",
"category": "दुकान",
"ownerName": "विनोद पाटील",
"location": "देवागाव",
"mobile": "9876543210",
"timing": "सकाळी 7 ते रात्री 10",
"description": "सर्व प्रकारच्या किराणा व घरगुती वस्तू उपलब्ध.",
"images": [],
"isOpen": true
}
Response
{ "success": true, "message": "व्यवसाय यशस्वीरित्या नोंदणी झाला.", "data": { "business": { "_id": "...", "name": "...", "category": "दुकान", "isOpen": true, "createdAt": "..." } } }
Lists businesses within the caller's own GP (super_admin may pass ?gp=<id>).
Query: category ('सर्व' or omitted = all categories), mine (true = only businesses I created), search (matches name/nameEn/ownerName/location), page, limit
Response
{ "success": true, "data": { "items": [ { "_id": "...", "name": "...", "nameEn": "...", "category": "दुकान", "ownerName": "...", "location": "...", "mobile": "...", "timing": "...", "description": "...", "images": [], "isOpen": true, "owner": { "_id": "...", "fullName": "...", "mobile": "..." }, "createdAt": "..." } ], "total": 6, "page": 1, "limit": 20 } }
Single business, with owner's name/mobile populated.
Quick उघडे/बंद toggle from the card, without resending the whole form.
Body
{ "isOpen": false }
Full edit — same body shape as POST, all fields optional (only sent fields are updated). Only the citizen who created the listing may edit it.
Only the citizen who created the listing may delete it.
Agriculture Prices
भाजीपाला / धान्य व कडधान्ये / फळे mandi prices and farming updates.
बाजारभाव (agri_veg_price_screen.dart) — type-wise tabs: भाजीपाला / धान्य व कडधान्ये / फळे. Each returned item includes an optional icon (emoji picked when the price was published; may be absent on older entries — the app falls back to a name-matched icon client-side).
Query: type (vegetable/grain/fruit), itemName, page (default 1), limit (default 20, max 100). Response: { items, total, page, limit } — AgriVegPriceScreen pages this per type tab (infinite scroll) rather than fetching every published price up front.
Single price entry.
Body
{ "type": "vegetable", "itemName": "Tomato", "icon": "🍅", "minPrice": 10, "maxPrice": 25, "modalPrice": 18 }icon is optional (a single emoji shown next to the item in the app) — omit it and the item still saves normally.
Edit a price entry — a gp_admin may only edit their own GP's entry (or a global entry they published themselves); super_admin can edit any. Accepts the same fields as POST, including icon.
Same ownership rule as PUT.
Farming updates feed.
Electricity Schedule (लाईट पुरवठा)
Power-cut / scheduled outage notices, plus feeder-wise शेती वीज वेळापत्रक (farming electricity schedule).
Powers ElectricityPowercutScreen's add/edit sheet. plannedType distinguishes नियोजित (planned) vs अनियोजित (unplanned) outages shown in the screen's filter tabs; status tracks planned/ongoing/completed.
Body
{ "area": "Ward 2", "type": "powercut", "title": "मुख्य फीडर देखभाल", "plannedType": "planned", "status": "planned", "date": "2026-08-20", "startTime": "10:00", "endTime": "14:00", "reason": "Maintenance", "affectedHomes": 120 }Query: type (scheduled/powercut/farming_connection)
Edit/reschedule/cancel a notice.
शेती वीज वेळापत्रक (Farming feeder schedule)
Powers ElectricityFarmingScreen (list) + ElectricityFarmingAddScreen (create). One document per फीडर, with the villages it covers, applicable weekdays, and one or more daily time slots.
Feeders for the caller's GP, newest first.
Query: page (default 1), limit (default 20, max 100). Response: { items, total, page, limit } — use total to know when to stop requesting further pages (infinite scroll on ElectricityFarmingScreen).
Body
{
"name": "फीडर A - उत्तर",
"nameEn": "Feeder A - North",
"villages": "देवागाव, पिंपळगाव, सोनगाव",
"colorHex": "#2E7D32",
"days": ["सोम", "मंगळ", "बुध", "गुरु", "शुक्र", "शनि", "रवि"],
"slots": [
{ "label": "पहाटे", "start": "05:00", "end": "09:00" },
{ "label": "दुपार", "start": "14:00", "end": "18:00" }
]
}
totalHours is computed server-side from the slots — no need to send it.
Water Supply (पाणीपुरवठा)
Powers WaterSupplyScreen (day-tab recurring weekly schedule) + WaterSupplyAddScreen (create). Supports two entry shapes in one collection: recurring weekly (day/days) for the normal schedule, and one-off dated entries (date) for things like a tanker visit or an outage update.
Query: day (e.g. सोमवार — recurring schedule for that weekday), status, from, to (dated entries), page (default 1), limit (default 20, max 100). Response: { items, total, page, limit } — total tells the client when to stop paging. WaterSupplyScreen pages this per selected day-tab (infinite scroll) rather than fetching the whole schedule up front.
Recurring weekly — pass days (array) to create one entry per day in a single call:
{ "area": "वॉर्ड क्र. 1 - गावठाण", "days": ["सोमवार", "गुरुवार"], "startTime": "06:30 AM", "endTime": "08:00 AM", "notes": "...", "isGeneral": false }
One-off dated entry (tanker visit / outage) — pass date instead of day/days:
{ "area": "वॉर्ड क्र. 2", "source": "tanker", "date": "2026-08-29", "startTime": "09:00", "status": "scheduled", "notes": "टँकरने पाणीपुरवठा" }Edit / mark ongoing / mark completed / reschedule.
Chat / Messages (मेसेज)
One-to-one Instagram-DM-style chat between citizens of the same Gram Panchayat. Two dedicated collections — Conversation (one doc per pair of users, holds the inbox preview + per-user unread count) and Message (one doc per message) — see src/models/Chat.js. No websocket server is required: the app polls GET /messages and GET /typing every couple of seconds while a chat screen is open, which is enough for a phone-network chat experience without extra infra.
People you can start a chat with — every citizen in your own village (GP), optionally filtered by name. Powers the "नवीन मेसेज" (new message) picker.
Get-or-create the 1-to-1 conversation with a person. Idempotent — call every time before opening a chat window.
Body
{ "userId": "<peer user id>" }
Response
{ "conversation": { "_id": "...", "peer": { "_id": "...", "fullName": "...", "profileImage": "..." }, "lastMessageText": null, "unreadCount": 0 } }Inbox list, newest activity first. Each item includes the other participant (peer), a denormalized lastMessageText/lastMessageAt, and your own unreadCount for that thread. Supports ?page=&limit=.
Message history, returned oldest→newest for the page. Pass before=<oldest message id you already have> to load older history (infinite-scroll-up). Also flips any messages sent to you from sent → delivered.
Send a message. For image/video/document, upload the file first via POST /api/uploads and pass the returned URL as mediaUrl.
Body
{ "type": "text", "text": "नमस्कार!", "replyTo": "<optional message id>" }
{ "type": "image", "mediaUrl": "https://.../a.jpg" }Marks every message sent to you in this thread as read (blue double-tick) and resets your unread counter. Call when the chat screen opens / comes to foreground.
Debounced heartbeat (e.g. every 2s while the input has focus and text). Expires automatically after ~4s.
Returns { "isTyping": true|false } for the other participant. Poll this every ~2s while the chat screen is open to show "टाइप करत आहे…".
Toggle an emoji reaction (double-tap ❤️ or long-press reaction bar). Sending the same emoji again removes it; sending a different one replaces your previous reaction on that message.
Body
{ "emoji": "❤️" }Without forEveryone: hides the message for you only. With forEveryone=true: sender-only, replaces the bubble with a "message deleted" placeholder for both people.
Notifications
Returns items, total and unreadCount.
File Uploads
Local disk storage under /uploads, served statically. Swap for S3/Cloud storage in production if you need CDN-backed media.
multipart/form-data, field name file. Max size configurable via MAX_UPLOAD_MB.
Response
{ "success": true, "data": { "url": "https://api.yourdomain.com/uploads/xyz.jpg" } }multipart/form-data, field name files (up to 10).
GP Admin Panel
Dashboard, own GP profile, about-us (type-wise facility info), notable persons, tourist places, gram body members, user management, points, birthdays, notifications, contacts, ration/beneficiary lists — everything in the gp_admin_home_screen and its sub-screens. All routes below require role gp_admin or super_admin and are automatically scoped to req.user.gp.
Summary counters: citizens, pending complaints, pending certificates, tax dues.
VillageProfileScreen — the gp_admin's own Gram Panchayat's full details (सरपंच, लोकसंख्या, संपर्क, फोटो इ.). Scoped to req.user.gp — no id is taken from the client.
VillageProfileScreen — edit the gp_admin's own GP profile. Accepts any subset of the editable fields below; other keys are silently ignored.
Body
{ "nameMarathi": "देवागाव ग्रामपंचायत", "nameEnglish": "Devgaon", "district": "पुणे", "taluka": "मुळशी", "village": "देवागाव", "state": "महाराष्ट्र", "pincode": "412108", "sarpanchName": "...", "sarpanchPhoto": "https://.../uploads/xyz.jpg", "sarpanchContact": "9876500001", "upsarpanchName": "...", "adminName": "...", "population": 4200, "area": "12.5 चौ.कि.मी.", "establishedYear": 1962, "contactNumber": "9876500000", "email": "gp@example.com", "address": "...", "logo": "https://.../uploads/logo.jpg", "coverImage": "https://.../uploads/cover.jpg", "about": "..." }
adminMobile and isActive are not editable here — mobile changes must go through /api/super-admin/gps/:id (keeps the linked login account in sync) and activation status is a platform-level decision. nameMarathi/district/taluka cannot be sent empty.
manage_users_screen — list/search users of this GP. Returns citizen, gp_admin, and every GP-staff designation (लाईटमन, तलाठी, ग्रामसेवक, इ.) — a user tagged with a staff role no longer disappears from this list once promoted. Only a platform-level super_admin account is excluded.
Query: search (matches fullName or mobile, case-insensitive — e.g. मोबाईल क्रमांकावरून शोधा), role (filter to one role, e.g. talathi), page, limit (default 20, max 100)
manage_users_screen — assign a role to a user within this gp_admin's own Gram Panchayat. Assignable roles: citizen, gp_admin, lightman, water_tax_collector, gram_sevak, talathi, arogya_sevika, anganwadi_sevika (see GP_ASSIGNABLE_ROLES in src/utils/roles.js). super_admin, news and kotwal are not assignable from this route — only from the Super Admin panel. A gp_admin cannot change their own role, and the target user must already belong to the caller's GP.
Body
{ "role": "talathi" }
The user gets a notification and can then log in with their own mobile via the usual request-otp / verify-otp flow — their token will carry the new role.
tax_payments_screen (bulk add) — resolve a citizen by mobile, auto-creating a bare record (scoped to this GP) if they haven't logged in yet.
Body
{ "mobile": "9876543210", "fullName": "Ramesh Patil" }award_points_screen.
Body
{ "points": 10, "reason": "Cleanliness drive participation" }birthday_screen — citizens with a birthday in the given month.
send_notifications_screen.
Body
{ "title": "Water supply notice", "body": "...", "userIds": [] }
userIds optional — omit to notify every citizen in the GP.
important_contacts_screen (admin management view — this GP's contacts only). Paginated via paginate() — response is { items, total, page, limit }; the admin screen loads more rows as the gp_admin scrolls instead of fetching the whole contact book at once.
Query: page, limit
Body
{ "name": "प्रभाकर सोनवणे", "designation": "ग्रामसेवक", "phone": "9876500003", "category": "government" }Edit any field — name, designation, phone, category.
VillageProfileScreen → "आमच्याबद्दल" (admin management view) — this GP's type-wise facility entries.
Query: category (optional — शाळा, आरोग्य केंद्र, रस्ते, स्मशानभूमी, मंदिरे, शासकीय इमारती व ग्रामपंचायत कार्यालये, इतर)
Body
{ "category": "शाळा", "title": "जिल्हा परिषद प्राथमिक शाळा", "info": "इयत्ता १ ली ते ७ वी, विद्यार्थी संख्या ८५", "photo": "https://.../uploads/school1.jpg" }
category is required (must be one of the fixed categories above); at least one of title, info, photo must be given.
Edit any field — category, title, info, photo.
VillageProfileScreen → "गौरवशाली व्यक्ती" (admin management view).
Query: type (optional — माजी सैनिक, शिक्षक, सरपंच, इतर)
Body
{ "type": "शिक्षक", "name": "श्री. अमोल देशमुख", "info": "निवृत्त मुख्याध्यापक, ३५ वर्षे सेवा", "photo": "https://.../uploads/person1.jpg" }
name is required. type defaults to इतर if omitted.
Edit any field — type, name, info, photo.
VillageProfileScreen → "पर्यटन स्थळे" (admin management view).
Body
{ "name": "गणपती मंदिर तलाव परिसर", "description": "गावाच्या पूर्वेला १ कि.मी., पावसाळ्यात नयनरम्य दृश्य", "photo": "https://.../uploads/place1.jpg" }
name is required.
Edit any field — name, description, photo.
VillageProfileScreen → "ग्राम बॉडी" (admin management view).
Query: type (optional — सरपंच, उपसरपंच, सदस्य, ग्रामसेवक, कोषाध्यक्ष, इतर)
Body
{ "type": "सदस्य", "name": "श्रीमती सुनिता जाधव", "contact": "9876500002", "photo": "https://.../uploads/member1.jpg" }
name is required. type defaults to सदस्य if omitted.
Edit any field — type, name, contact, photo.
rationing_screen.
Body
{ "user": "<citizen userId>", "cardNumber": "AAY-00123", "cardType": "APL", "familyMembers": 4, "shopName": "Fair Price Shop 1", "monthlyAllocation": "10kg गहू, 5kg तांदूळ" }
cardNumber is required; user is optional (leave unset for a household not yet linked to a citizen login).
labharthi_list_screen — citizens with an approved scheme application.
Complaints, certificates, tax, notices, development-works, schemes and jobs admin actions live under their own modules above (PATCH .../status endpoints) — gp_admin has access to all of them automatically.
Super Admin Panel
Platform-wide control: onboard/manage Gram Panchayats, create gp_admin accounts, view analytics and audit logs, broadcast notifications. All routes require role super_admin.
gp_management_screen — register a new Gram Panchayat. nameMarathi, district, taluka and village are required. If adminMobile is given, a matching gp_admin User account is created (or an existing user with that mobile is linked/promoted) so the admin can log in immediately via OTP — no separate call to /gp-admins needed.
Body
{
"nameMarathi": "देवागाव ग्रामपंचायत",
"district": "पुणे",
"taluka": "भोर",
"village": "देवागाव",
"adminName": "रमेश पाटील",
"adminMobile": "9876500000",
"isActive": true,
"sarpanchName": "...",
"contactNumber": "..."
}
Response
{ "gp": { ... }, "admin": { "_id": "...", "mobile": "9876500000", "role": "gp_admin", "gp": "665f1..." } }
Example (curl, with token)
curl -X POST https://your-domain/api/super-admin/gps \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{ "nameMarathi": "देवागाव ग्रामपंचायत", "district": "पुणे", "taluka": "भोर", "village": "देवागाव", "adminName": "रमेश पाटील", "adminMobile": "9876500000" }'Query: search, district, taluka, village, isActive, page, limit
Update GP details. Sending adminMobile keeps the linked gp_admin User in sync.
Sets isActive: false — citizens in this village will then see the "ग्रामपंचायत उपलब्ध नाही" message until reactivated.
gp_admin_screen (super-admin app) — create a gp_admin login for a GP.
Body
{ "mobile": "9876500000", "fullName": "Suresh Kale", "gp": "665f1..." }user_role_screen — every user in the system, any role, any GP.
Query: role (any value from the role registry — citizen, gp_admin, super_admin, lightman, water_tax_collector, news, gram_sevak, talathi, arogya_sevika, kotwal, anganwadi_sevika), gp, search (matches fullName or mobile)
Body
{ "isActive": false }user_role_screen — a super_admin can assign any role to any user (unlike the GP Admin panel's route, which is limited to a smaller assignable subset — see SUPER_ADMIN_ASSIGNABLE_ROLES in src/utils/roles.js).
Body
{ "role": "kotwal" }analytics_screen — platform-wide counts: GPs, citizens, gp_admins, complaint/certificate breakdowns, tax totals.
audit_screen — every sensitive super-admin action is logged here.
broadcast_screen — send a notification to all citizens, optionally scoped to one GP.
Body
{ "title": "Maintenance notice", "body": "App will be down for maintenance tonight.", "gp": null }
Social Feed (Gavkatta / Reels)
Village social feed: posts (image/video/carousel), likes, comments, shares, 24-hour stories.
Body
Village feed, newest first, includes
isLikedper-user.Toggles like on/off.
Body
Body
Active (non-expired) stories, auto-expire after 24h.