API Reference β Overview
The CivicPulse REST API is a JSON-over-HTTP API served by the Express backend. All endpoints are prefixed with /api.
Base URLβ
| Environment | Base URL |
|---|---|
| Local development | http://localhost:3001/api |
| Production | https://api.civicpulse.in/api |
Authenticationβ
Most write endpoints and some read endpoints require a valid JWT access token.
Header format:
Authorization: Bearer <accessToken>
Access tokens are issued by POST /api/auth/google and refreshed via POST /api/auth/refresh. They expire after 15 minutes. The refresh token is stored as an httpOnly cookie and is valid for 7 days.
Endpoints that require authentication are marked with a lock icon or note in their respective sections.
Response Envelopeβ
All API responses follow a consistent envelope:
Success:
{
"success": true,
"data": { }
}
Error:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "title is required",
"details": [
{ "field": "title", "message": "title must not be empty" }
]
}
}
Common error codes:
| Code | HTTP Status | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | Request body/params failed validation |
UNAUTHORIZED | 401 | Missing or expired access token |
FORBIDDEN | 403 | Valid token but insufficient role |
NOT_FOUND | 404 | Resource does not exist |
CONFLICT | 409 | Duplicate action (e.g. upvoting twice) |
RATE_LIMITED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Unexpected server error |
Paginated Responsesβ
List endpoints that can return large result sets use DynamoDB-style cursor pagination:
{
"success": true,
"data": {
"count": 20,
"items": [ ],
"nextKey": "eyJQSyI6Ikl...",
"_links": {
"self": "/api/issues?city=mumbai&limit=20",
"next": "/api/issues?city=mumbai&limit=20&lastKey=eyJQSyI6Ikl..."
}
}
}
| Field | Type | Description |
|---|---|---|
count | number | Number of items in this page |
items | array | The result records |
nextKey | string | null | Opaque cursor for the next page; null if this is the last page |
_links.next | string | null | Ready-to-use URL for the next page |
To fetch subsequent pages, pass ?lastKey=<nextKey> as a query parameter.
Rate Limitsβ
| Scope | Limit | Window |
|---|---|---|
General (/api/*) | 100 requests | 15 minutes per IP |
Auth (/api/auth/*) | 20 requests | 15 minutes per IP |
When the limit is exceeded, the API responds with HTTP 429 and the following headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705312200
Retry-After: 847
Machine-Readable API Indexβ
GET /api/ returns a JSON document listing all available routes, valid enum values, and pagination parameters. This is useful for building API clients or integration tests without reading documentation.
curl http://localhost:3001/api/
See Discovery & Enums for details.
Postman Collectionβ
The fastest way to explore the API is with the official Postman collection. It covers all 41 endpoints, includes pre-request auth scripts that auto-save the access token after login, and ships ready-to-use environments for local dev and production.
Downloadβ
π Download CivicPulse_API.postman_collection.json
Or clone the repo and find it at postman/CivicPulse_API.postman_collection.json.
Import & run in 3 stepsβ
Step 1 β Import
Postman β File β Import β select CivicPulse_API.postman_collection.json
Or drag the file into the Postman window.
Step 2 β Set the base URL
Open the collection's Variables tab and set baseUrl:
| Environment | Value |
|---|---|
| Local dev | http://localhost:3001/api |
| Production | https://api.civicpulse.in/v1 |
Step 3 β Authenticate
- Open
π Auth β Google Sign-In. - Replace
<google-id-token>in the body with a real Google ID token. - Send β the test script auto-saves the
accessTokencollection variable. - All subsequent requests are automatically authenticated via the collection-level bearer token.
Run with Newman (CLI)β
Newman lets you run the collection in CI/CD:
# Install Newman globally
npm install -g newman
# Run the full collection against local dev
newman run postman/CivicPulse_API.postman_collection.json \
--env-var "baseUrl=http://localhost:3001/api" \
--env-var "accessToken=<your-token>"
# Pretty HTML report
npm install -g newman-reporter-html
newman run postman/CivicPulse_API.postman_collection.json \
--reporters html \
--reporter-html-export newman-report.html
Collection variablesβ
| Variable | Default | Description |
|---|---|---|
baseUrl | http://localhost:3001/api | API root β change per environment |
accessToken | (auto-set on login) | JWT access token |
issueId | ISS-001 | Issue ID used in parameterised requests |
userId | u1 | User ID for score/role endpoints |
mediaId | (auto-set on presign) | Media ID from presign response |
messageId | (auto-set on post) | Message ID from post response |
reqId | Reopen-request ID for review endpoints |
Endpoint Groupsβ
| Group | Base Path | Description |
|---|---|---|
| Authentication | /api/auth | Google OAuth login, token refresh, logout |
| Issues | /api/issues | CRUD for civic issues, upvotes, assignment, bulk status, stats |
| Resolution Flow | /api/issues/:id/resolve | Resolve with proof, community rating, reopen requests, close |
| Messages | /api/issues/:id/messages | Per-issue chat thread and reactions |
| Media | /api/issues/:id/media | S3 presigned uploads and Twitter URL attachments |
| Scores | /api/scores | Contributor leaderboard and individual score |
| Admin | /api/admin, /api/users | User role management, reopen queue, flagged issues |
| Accountability | /api/accountability-chain, /api/jurisdictions, /api/officials | Accountability chain resolution, official hierarchy, departments, police stations |
| X (Twitter) | /api/x/mentions | X API v2 mentions feed, tweet β issue conversion |
/api/whatsapp/webhook, /api/whatsapp/messages | WhatsApp Business API webhook, message β issue conversion | |
/api/ig/mentions | Instagram Graph API mentions feed, post β issue conversion | |
| Discovery | /api/, /api/cities, /api/categories | API index, city list, category list |
Health Checkβ
GET /api/health
Returns server uptime and timestamp. No authentication required. Use this endpoint for load balancer health probes.
{
"status": "ok",
"uptime": 14523.4,
"timestamp": "2025-01-15T10:30:00.000Z"
}