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 }.

Base URL: your-domain/api Auth: Bearer JWT Login: Mobile + OTP
Authentication. Every protected route expects Authorization: Bearer <accessToken>. Get a token pair via POST /api/auth/request-otpPOST /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.
RoleCan do
citizenUse all citizen-facing services: complaints, certificates, tax, schemes, jobs, marketplace, social feed — scoped to their own Gram Panchayat.
gp_adminEverything 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_adminOnboard/manage Gram Panchayats, create gp_admin accounts, view platform-wide analytics and audit logs, broadcast notifications.
lightmanGP-staff designation (लाईटमन). Assignable by gp_admin or super_admin.
water_tax_collectorGP-staff designation (पाणी कर संग्राहक). Assignable by gp_admin or super_admin.
gram_sevakGP-staff designation (ग्रामसेवक). Assignable by gp_admin or super_admin.
talathiGP-staff designation (तलाठी). Assignable by gp_admin or super_admin.
arogya_sevikaGP-staff designation (आरोग्य सेविका). Assignable by gp_admin or super_admin.
anganwadi_sevikaGP-staff designation (अंगणवाडी सेविका). Assignable by gp_admin or super_admin.
newsGP-staff designation (न्यूज / वार्ताहर). Super-admin only — not assignable from the GP Admin panel.
kotwalGP-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.

POST/api/auth/request-otpPublic

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.

POST/api/auth/verify-otpPublic

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 }
  }
}
POST/api/auth/refresh-tokenPublic

Exchange a refresh token for a fresh access token.

Body

{ "refreshToken": "..." }
GET/api/auth/meAny logged-in user

Returns the currently authenticated user.

POST/api/auth/logoutAny logged-in user

Stateless logout — client discards tokens locally.

Super Admin — quick start (dev mode).
  1. Run npm run seed once — creates/promotes the user at SUPER_ADMIN_MOBILE (default 9999999999) to role super_admin.
  2. POST /api/auth/request-otp with { "mobile": "9999999999" }. With OTP_DEV_MODE=true the response includes "devOtp": "123456" — no SMS is actually sent.
  3. POST /api/auth/verify-otp with { "mobile": "9999999999", "otp": "123456" } → response has accessToken and user.role: "super_admin".
  4. Send that token as Authorization: Bearer <accessToken> on every /api/super-admin/* and /api/locations/import call 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/api/profileAny logged-in user

Get the logged-in user's full profile.

PUT/api/profileAny logged-in user

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).

GET/api/locations/districtsPublic

Sorted list of all districts.

GET/api/locations/talukasPublic

Query: district (required)

GET/api/locations/villagesPublic

Query: district, taluka (both required)

POST/api/locationssuper_admin

Add a single district/taluka/village row.

Body

{ "district": "पुणे", "taluka": "भोर", "village": "देवागाव" }
POST/api/locations/importsuper_admin

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.

DELETE/api/locations/:idsuper_admin

Deactivate (soft-delete) one row.

Villages / Gram Panchayat Info

Village profile, sarpanch info, GP members — public read, managed by super_admin.

GET/api/villagesPublic

List/search Gram Panchayats for the citizen onboarding "select your village" step.

Query: search, district, taluka, page, limit

GET/api/villages/checkPublic

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": "देवागाव ग्रामपंचायत", ... } }
GET/api/villages/:idPublic

Village profile screen: sarpanch info, about, contact, cover image.

GET/api/villages/:id/membersAny logged-in user

GP staff (gp_admin users) tied to this Gram Panchayat.

GET/api/villages/:id/contactsAny logged-in user

महत्वाचे संपर्क (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

GET/api/villages/:id/about-infoAny logged-in user

आमच्याबद्दल (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)

GET/api/villages/:id/notable-personsAny logged-in user

गौरवशाली व्यक्ती (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 — माजी सैनिक, शिक्षक, सरपंच, इतर)

GET/api/villages/:id/tourist-placesAny logged-in user

पर्यटन स्थळे (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.

GET/api/villages/:id/gram-bodyAny logged-in user

ग्राम बॉडी (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.

POST/api/complaintscitizen

Submit a complaint.

Body

{ "type": "water", "title": "No water supply", "description": "...", "images": ["url1"], "location": "Ward 4", "ward": "4" }
GET/api/complaintsAny logged-in user

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

GET/api/complaints/:idOwner / admin

Single complaint details.

PATCH/api/complaints/:idOwner (while pending) / admin

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" }
PATCH/api/complaints/:id/statusgp_admin / super_admin

Update status, resolution note/photos, assignee, priority. Backs the "पूर्ण करा" (mark resolved) action on ResolveComplaintsScreen.

Body

{ "status": "resolved", "resolutionNote": "Fixed the pipeline leak.", "resolutionImages": [] }
POST/api/complaints/:id/logsgp_admin / super_admin

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 }
DELETE/api/complaints/:idOwner / admin

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 (दाखले).

POST/api/certificatescitizen

Apply for a certificate.

Body

{
  "type": "birth",
  "applicantDetails": { "childName": "...", "dob": "2024-01-01", "fatherName": "...", "motherName": "..." },
  "documents": [{ "name": "hospital_slip", "url": "https://.../uploads/x.pdf" }]
}
GET/api/certificatesAny logged-in user

Query: type, status, mine, page, limit

GET/api/certificates/:idOwner / admin

Application status/details (certificate_status_screen).

PATCH/api/certificates/:id/statusgp_admin / super_admin

Review, approve/reject, or issue with a certificate number & PDF.

Body

{ "status": "issued", "certificateNumber": "GP/2026/0123", "certificateFile": "https://.../cert.pdf", "fee": 50 }
PATCH/api/certificates/:id/detailsOwner / gp_admin / super_admin

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.

POST/api/taxgp_admin / super_admin

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" }
GET/api/taxAny logged-in user

Query: type, status, mine, page, limit

GET/api/tax/summary/minecitizen

All of the logged-in citizen's own tax records grouped by propertyId - powers the "मालमत्ता निवडा" property picker and "मागील व्यवहार" payment history in one call.

GET/api/tax/:idOwner / admin

Tax status / house / water tax detail, including breakdown and paymentHistory (each entry now carries a receiptNo).

POST/api/tax/:id/payOwner / admin

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" }
PATCH/api/tax/:idgp_admin / super_admin

Edit a demand (propertyId, owner/occupant names, financialYear, breakdown, dueDate, status).

DELETE/api/tax/:idgp_admin / super_admin

Remove a wrongly-entered demand.

POST/api/tax/bulkgp_admin / super_admin

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.

POST/api/noticesgp_admin / super_admin

Body

{ "type": "gram_sabha", "titleMarathi": "ग्रामसभा सूचना", "description": "...", "eventDate": "2026-09-01" }
GET/api/noticesAny logged-in user

Query: type (announcement/order/gram_sabha/tax), page, limit

GET/api/notices/:idAny logged-in user
PUT/api/notices/:idgp_admin / super_admin
DELETE/api/notices/:idgp_admin / super_admin

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:

typeMarathiCitizen screengp_admin screen
jahirnamaजाहीरनामाnotice_announcements_screen.dartgp_admin/screens/gov_notice_jahirnama_screen.dart
shasan_adeshशासन आदेशnotice_orders_screen.dartgp_admin/screens/gov_notice_shasan_adesh_screen.dart
kar_noticeकर नोटीसnotice_tax_screen.dartgp_admin/screens/gov_notice_kar_notice_screen.dart
POST/api/gov-noticesgp_admin / super_admin

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.

GET/api/gov-noticesAny logged-in user

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

GET/api/gov-notices/:idAny logged-in user
PUT/api/gov-notices/:idgp_admin / super_admin

Edit any field from the create body above — pass only what changed.

DELETE/api/gov-notices/:idgp_admin / super_admin

GovNotice document fields

FieldTypeNotes
gpObjectIdOwning Gram Panchayat (auto, from session)
typeString enumRequired — jahirnama | shasan_adesh | kar_notice
titleStringRequired — शीर्षक
categoryStringFree-text; meaning depends on type (see admin form dropdowns)
departmentStringजारी करणारा विभाग/कार्यालय
noticeNoStringसूचना/आदेश क्रमांक
summaryStringछोटा सारांश (कार्ड प्रीव्ह्यू)
descriptionStringसविस्तर मजकूर
attachmentStringPDF/image URL from /api/uploads
tagsString[]Free-text tags
noticeDateDateजाहीर/सूचना दिनांक (डीफॉल्ट: आत्ताची वेळ)
effectiveDateDateप्रभावी दिनांक (मुख्यतः शासन आदेश)
deadlineDateDateमुदत/देय तारीख (मुख्यतः जाहीरनामा/कर नोटीस)
contactName / contactPhoneStringसंपर्क तपशील
isPinnedBooleanडीफॉल्ट false — pinned notices sort first
isActiveBooleanडीफॉल्ट truefalse = कालबाह्य/रद्द
publishedByObjectIdUser who created it (auto, from session)
createdAt / updatedAtDateAuto (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).

POST/api/gram-sabhagp_admin / super_admin

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)

GET/api/gram-sabhaAny logged-in user

Lists gram sabha notices for the caller's own GP, most recent meeting date first.

Query: status (upcoming/held/cancelled), page, limit

GET/api/gram-sabha/:idAny logged-in user
PUT/api/gram-sabha/:idgp_admin / super_admin

Edit any field from the create body above — pass only what changed.

DELETE/api/gram-sabha/:idgp_admin / super_admin

GramSabha document fields

FieldTypeNotes
gpObjectIdOwning Gram Panchayat (auto, from session)
titleStringRequired — सभेचे शीर्षक/विषय
categoryStringसभेचा प्रकार
venueStringRequired — सभेचे ठिकाण
meetingDateDateRequired — तारीख + वेळ एकत्र (ISO-8601)
noticeNoStringसूचना क्रमांक
agendaString[]अजेंडा विषयांची यादी
attendeesStringउपस्थितीचा तपशील
descriptionStringसभेचे वर्णन
statusString enumupcoming | held | cancelled
publishedByObjectIdUser who created it (auto, from session)
createdAt / updatedAtDateAuto (Mongoose timestamps)

Development Works

Village development works — ongoing/completed/planned, with budget, contractor and photos.

POST/api/development-worksgp_admin / super_admin
GET/api/development-worksAny logged-in user

Query: status (ongoing/completed/planned)

GET/api/development-works/:idAny logged-in user
PUT/api/development-works/:idgp_admin / super_admin
DELETE/api/development-works/:idgp_admin / super_admin

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.

POST/api/village-eventsAny logged-in user

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" }
GET/api/village-eventsAny logged-in user

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).

GET/api/village-events/:idAny logged-in user
PUT/api/village-events/:idOwner / gp_admin / super_admin

Only the citizen who posted the event, or a gp_admin/super_admin, may edit it — otherwise 403.

DELETE/api/village-events/:idOwner / gp_admin / super_admin

Same ownership rule as PUT.

Schemes (सरकारी योजना)

Government/GP schemes: info, eligibility check, apply, document upload, application review. Powers SchemeInfoScreen (list + detail), SchemeEligibilityScreen, SchemeDocumentsScreen, SchemeApplyScreen.

POST/api/schemesgp_admin / super_admin / talathi

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.

GET/api/schemesAny logged-in user

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.

GET/api/schemes/:idAny logged-in user
PUT/api/schemes/:idgp_admin / super_admin / talathi

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/api/schemes/:idgp_admin / super_admin / talathi

Delete a scheme. Same ownership rule as PUT. The scheme disappears from every user's list immediately.

GET/api/schemes/:id/eligibilityAny logged-in user

scheme_eligibility_screen data.

POST/api/schemes/:id/applycitizen

Body

{ "documents": [{ "name": "income_cert", "url": "https://.../doc.pdf" }] }
GET/api/schemes/applications/minecitizen
GET/api/schemes/applications/allgp_admin / super_admin

Query: status

PATCH/api/schemes/applications/:id/statusgp_admin / super_admin

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.

POST/api/karad-newsnews / gp_admin / super_admin

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.

GET/api/karad-newsAny logged-in user

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).

GET/api/karad-news/talukasAny logged-in user

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.

GET/api/karad-news/:idAny logged-in user

Full details of a single news item — backs KaradNewsDetailScreen (opened by tapping a card). Also increments the view counter.

PUT/api/karad-news/:idOwner (news role) / admin

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.

DELETE/api/karad-news/:idOwner (news role) / admin

Same owner+role rule as PUT above.

POST/api/karad-news/:id/likeAny logged-in user

Toggles like. Response: { "liked": true, "likeCount": 42 }

POST/api/karad-news/:id/shareAny logged-in user

Bumps the share counter.

Job Portal

Citizen job board — post (JobCreateScreen), approve (gp_admin moderation), browse (JobListScreen), view (JobDetailScreen), apply (JobApplyScreen).

POST/api/jobsAny logged-in user

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

GET/api/jobsAny logged-in user

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

GET/api/jobs/applications/mineAny logged-in user

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.

GET/api/jobs/:idAny logged-in user

Single job details for JobDetailScreen.

PATCH/api/jobs/:id/approvegp_admin / super_admin

Backs ApproveJobsScreen's मंजूर/नाकारा actions.

Body

{ "approvalStatus": "approved" }
PUT/api/jobs/:idOwner / admin

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.

DELETE/api/jobs/:idOwner / admin

Deletes the job and cascades to remove its JobApplication records.

POST/api/jobs/:id/applycitizen

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": "..."
}
GET/api/jobs/:id/applicationsJob owner / admin

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.

POST/api/marketplacecitizen

Body

{ "category": "farming", "title": "Fresh onions", "price": 25, "unit": "per kg", "images": [] }
GET/api/marketplaceAny logged-in user

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.

GET/api/marketplace/:idAny logged-in user
PUT/api/marketplace/:idOwner / admin
DELETE/api/marketplace/:idOwner / admin

Animal Marketplace

Buy/sell livestock listings (animal_buy_screen, animal_sell_screen, animal_marketplace_screen).

POST/api/animal-marketplacecitizen

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.

GET/api/animal-marketplaceAny logged-in user

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.

GET/api/animal-marketplace/:idAny logged-in user
PUT/api/animal-marketplace/:idOwner
DELETE/api/animal-marketplace/:idOwner

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.

POST/api/local-businesscitizen

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": "..." } } }
GET/api/local-businessAny logged-in user

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 } }
GET/api/local-business/:idAny logged-in user

Single business, with owner's name/mobile populated.

PATCH/api/local-business/:id/statusOwner

Quick उघडे/बंद toggle from the card, without resending the whole form.

Body

{ "isOpen": false }
PUT/api/local-business/:idOwner

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.

DELETE/api/local-business/:idOwner

Only the citizen who created the listing may delete it.

Agriculture Prices

भाजीपाला / धान्य व कडधान्ये / फळे mandi prices and farming updates.

GET/api/agri/pricesAny logged-in user

बाजारभाव (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.

GET/api/agri/prices/:idAny logged-in user

Single price entry.

POST/api/agri/pricesgp_admin / super_admin / kotwal

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.

PUT/api/agri/prices/:idgp_admin / super_admin / kotwal

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.

DELETE/api/agri/prices/:idgp_admin / super_admin / kotwal

Same ownership rule as PUT.

GET/api/agri/updatesAny logged-in user

Farming updates feed.

POST/api/agri/updatesgp_admin / super_admin

Electricity Schedule (लाईट पुरवठा)

Power-cut / scheduled outage notices, plus feeder-wise शेती वीज वेळापत्रक (farming electricity schedule).

POST/api/electricitygp_admin / super_admin / lightman

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 }
GET/api/electricityAny logged-in user

Query: type (scheduled/powercut/farming_connection)

PATCH/api/electricity/:idgp_admin / super_admin / lightman

Edit/reschedule/cancel a notice.

DELETE/api/electricity/:idgp_admin / super_admin / lightman

शेती वीज वेळापत्रक (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.

GET/api/electricity/farmingAny logged-in user

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).

POST/api/electricity/farminggp_admin / super_admin / lightman

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.

DELETE/api/electricity/farming/:idgp_admin / super_admin / lightman

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.

GET/api/water-supplyAny logged-in user

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.

POST/api/water-supplygp_admin / super_admin / water_tax_collector

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": "टँकरने पाणीपुरवठा" }
GET/api/water-supply/:idAny logged-in user
PATCH/api/water-supply/:idgp_admin / super_admin / water_tax_collector

Edit / mark ongoing / mark completed / reschedule.

DELETE/api/water-supply/:idgp_admin / super_admin / water_tax_collector

Social Feed (Gavkatta / Reels)

Village social feed: posts (image/video/carousel), likes, comments, shares, 24-hour stories.

POST/api/social/postscitizen

Body

{ "type": "image", "mediaUrls": ["https://.../a.jpg"], "caption": "...", "hashtags": ["gaon"] }
GET/api/social/postsAny logged-in user

Village feed, newest first, includes isLiked per-user.

GET/api/social/posts/:idAny logged-in user
DELETE/api/social/posts/:idOwner / admin
POST/api/social/posts/:id/likeAny logged-in user

Toggles like on/off.

POST/api/social/posts/:id/shareAny logged-in user
POST/api/social/posts/:id/commentsAny logged-in user

Body

{ "text": "Great post!" }
GET/api/social/posts/:id/commentsAny logged-in user
DELETE/api/social/comments/:idOwner / admin
POST/api/social/storiescitizen

Body

{ "mediaUrl": "https://.../story.jpg", "caption": "..." }
GET/api/social/storiesAny logged-in user

Active (non-expired) stories, auto-expire after 24h.

POST/api/social/stories/:id/viewAny logged-in user

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.

GET/api/chat/users?search=citizen

People you can start a chat with — every citizen in your own village (GP), optionally filtered by name. Powers the "नवीन मेसेज" (new message) picker.

POST/api/chat/conversations/startAny logged-in user

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 } }
GET/api/chat/conversationsAny logged-in user

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=.

GET/api/chat/conversations/:id/messages?before=&limit=30Participant only

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 sentdelivered.

POST/api/chat/conversations/:id/messagesParticipant only

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" }
PATCH/api/chat/conversations/:id/readParticipant only

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.

POST/api/chat/conversations/:id/typingParticipant only

Debounced heartbeat (e.g. every 2s while the input has focus and text). Expires automatically after ~4s.

GET/api/chat/conversations/:id/typingParticipant only

Returns { "isTyping": true|false } for the other participant. Poll this every ~2s while the chat screen is open to show "टाइप करत आहे…".

POST/api/chat/messages/:id/reactParticipant only

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": "❤️" }
DELETE/api/chat/messages/:id?forEveryone=trueParticipant only

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

GET/api/notificationsAny logged-in user

Returns items, total and unreadCount.

PATCH/api/notifications/:id/readAny logged-in user
PATCH/api/notifications/read-allAny logged-in user

File Uploads

Local disk storage under /uploads, served statically. Swap for S3/Cloud storage in production if you need CDN-backed media.

POST/api/uploadsAny logged-in user

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" } }
POST/api/uploads/multipleAny logged-in user

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.

GET/api/gp-admin/dashboard

Summary counters: citizens, pending complaints, pending certificates, tax dues.

GET/api/gp-admin/profile

VillageProfileScreen — the gp_admin's own Gram Panchayat's full details (सरपंच, लोकसंख्या, संपर्क, फोटो इ.). Scoped to req.user.gp — no id is taken from the client.

PUT/api/gp-admin/profile

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.

GET/api/gp-admin/users

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)

PATCH/api/gp-admin/users/:id/role

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.

POST/api/gp-admin/users/find-or-create

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" }
PATCH/api/gp-admin/users/:id/points

award_points_screen.

Body

{ "points": 10, "reason": "Cleanliness drive participation" }
GET/api/gp-admin/birthdays?month=8

birthday_screen — citizens with a birthday in the given month.

POST/api/gp-admin/notifications

send_notifications_screen.

Body

{ "title": "Water supply notice", "body": "...", "userIds": [] }

userIds optional — omit to notify every citizen in the GP.

GET/api/gp-admin/contacts

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

POST/api/gp-admin/contacts

Body

{ "name": "प्रभाकर सोनवणे", "designation": "ग्रामसेवक", "phone": "9876500003", "category": "government" }
PUT/api/gp-admin/contacts/:id

Edit any field — name, designation, phone, category.

DELETE/api/gp-admin/contacts/:id
GET/api/gp-admin/about-info

VillageProfileScreen → "आमच्याबद्दल" (admin management view) — this GP's type-wise facility entries.

Query: category (optional — शाळा, आरोग्य केंद्र, रस्ते, स्मशानभूमी, मंदिरे, शासकीय इमारती व ग्रामपंचायत कार्यालये, इतर)

POST/api/gp-admin/about-info

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.

PUT/api/gp-admin/about-info/:id

Edit any field — category, title, info, photo.

DELETE/api/gp-admin/about-info/:id
GET/api/gp-admin/notable-persons

VillageProfileScreen → "गौरवशाली व्यक्ती" (admin management view).

Query: type (optional — माजी सैनिक, शिक्षक, सरपंच, इतर)

POST/api/gp-admin/notable-persons

Body

{ "type": "शिक्षक", "name": "श्री. अमोल देशमुख", "info": "निवृत्त मुख्याध्यापक, ३५ वर्षे सेवा", "photo": "https://.../uploads/person1.jpg" }

name is required. type defaults to इतर if omitted.

PUT/api/gp-admin/notable-persons/:id

Edit any field — type, name, info, photo.

DELETE/api/gp-admin/notable-persons/:id
GET/api/gp-admin/tourist-places

VillageProfileScreen → "पर्यटन स्थळे" (admin management view).

POST/api/gp-admin/tourist-places

Body

{ "name": "गणपती मंदिर तलाव परिसर", "description": "गावाच्या पूर्वेला १ कि.मी., पावसाळ्यात नयनरम्य दृश्य", "photo": "https://.../uploads/place1.jpg" }

name is required.

PUT/api/gp-admin/tourist-places/:id

Edit any field — name, description, photo.

DELETE/api/gp-admin/tourist-places/:id
GET/api/gp-admin/gram-body

VillageProfileScreen → "ग्राम बॉडी" (admin management view).

Query: type (optional — सरपंच, उपसरपंच, सदस्य, ग्रामसेवक, कोषाध्यक्ष, इतर)

POST/api/gp-admin/gram-body

Body

{ "type": "सदस्य", "name": "श्रीमती सुनिता जाधव", "contact": "9876500002", "photo": "https://.../uploads/member1.jpg" }

name is required. type defaults to सदस्य if omitted.

PUT/api/gp-admin/gram-body/:id

Edit any field — type, name, contact, photo.

DELETE/api/gp-admin/gram-body/:id
GET/api/gp-admin/ration-entries

rationing_screen.

POST/api/gp-admin/ration-entries

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).

GET/api/gp-admin/beneficiaries

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.

POST/api/super-admin/gps

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" }'
GET/api/super-admin/gps

Query: search, district, taluka, village, isActive, page, limit

PUT/api/super-admin/gps/:id

Update GP details. Sending adminMobile keeps the linked gp_admin User in sync.

PATCH/api/super-admin/gps/:id/deactivate

Sets isActive: false — citizens in this village will then see the "ग्रामपंचायत उपलब्ध नाही" message until reactivated.

PATCH/api/super-admin/gps/:id/activate
POST/api/super-admin/gp-admins

gp_admin_screen (super-admin app) — create a gp_admin login for a GP.

Body

{ "mobile": "9876500000", "fullName": "Suresh Kale", "gp": "665f1..." }
GET/api/super-admin/users

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)

PATCH/api/super-admin/users/:id/status

Body

{ "isActive": false }
PATCH/api/super-admin/users/:id/role

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" }
GET/api/super-admin/analytics

analytics_screen — platform-wide counts: GPs, citizens, gp_admins, complaint/certificate breakdowns, tax totals.

GET/api/super-admin/audit-logs

audit_screen — every sensitive super-admin action is logged here.

POST/api/super-admin/broadcast

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 }