🔌 API Documentation

Base URL: https://tenders.ramram.in/api

Version: 1.0 MVP | Last Updated: December 2025

⚠️ Authentication Required: All endpoints (except login) require JWT authentication via Bearer token in the Authorization header.

📋 Table of Contents

🔐 Authentication

POST /auth/login

Description: Authenticate user and receive JWT token

Request Body:

{
  "email": "[email protected]",
  "password": "admin123"
}

Response (200 OK):

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": 1,
    "email": "[email protected]",
    "name": "Admin User",
    "role": "admin"
  }
}

Errors:

POST /auth/logout

Description: Logout (client-side token removal)

Response (200 OK):

{
  "message": "Logged out successfully"
}

📁 Tenders

GET /tenders

Description: List all tenders with optional filtering

Headers: Authorization: Bearer {token}

Query Parameters:

Parameter Type Description
state string Filter by state (e.g., "Assam")
status string Filter by status (open, technical, financial, awarded)
search string Search in tender_no and title (case-insensitive)
limit number Results per page (default: 50)
offset number Pagination offset (default: 0)

Response (200 OK):

{
  "tenders": [
    {
      "id": 1,
      "tender_no": "TEST_2025_001",
      "title": "Road Construction Project",
      "authority": "PWD Assam",
      "state": "Assam",
      "district": "Kamrup",
      "closing_date": "2025-02-15T00:00:00.000Z",
      "status": "open",
      "created_at": "2025-12-30T04:56:03.627Z"
    }
  ],
  "limit": 50,
  "offset": 0
}

POST /tenders

Description: Create a new tender

Headers: Authorization: Bearer {token}

Request Body:

{
  "tender_no": "MMPPNA_23_24_236",
  "title": "Construction of RCC Culverts",
  "authority": "PWD Assam",
  "state": "Assam",
  "district": "Cachar",
  "closing_date": "2025-01-31",
  "estimated_value": 5000000,
  "status": "open",
  "source_url": "https://..."
}

Response (201 Created):

{
  "id": 2,
  "tender_no": "MMPPNA_23_24_236",
  "title": "Construction of RCC Culverts",
  ...
  "created_at": "2025-12-30T05:00:00.000Z"
}

Errors:

GET /tenders/:id

Description: Get single tender by ID

Headers: Authorization: Bearer {token}

Response (200 OK): Tender object

Errors: 404 - Tender not found

PUT /tenders/:id

Description: Update tender (partial update supported)

Headers: Authorization: Bearer {token}

Request Body: Any tender fields to update

{
  "status": "awarded",
  "closing_date": "2025-02-28"
}

Response (200 OK): Updated tender object

📤 File Upload

POST /tenders/:tenderId/upload

Description: Upload BOQ file (ZIP/Excel) for processing

Headers: Authorization: Bearer {token}

Content-Type: multipart/form-data

Form Data:

Constraints:

Response (201 Created):

{
  "id": 1,
  "tender_id": 1,
  "file_type": "zip",
  "original_filename": "work_59027.zip",
  "storage_path": "/opt/claude/tenders/data/uploads/tender_1/...",
  "file_size": 125669,
  "checksum": "9b05547942dbd15208c1b34819e2c3049aa400494674a25c587516feb6976a65",
  "uploaded_at": "2025-12-30T04:56:40.728Z"
}

Background Processing:

File processing happens automatically in background. BOQ data will be available within 5-30 seconds depending on file size.

Errors:

GET /tenders/:tenderId/files

Description: List uploaded files for a tender

Headers: Authorization: Bearer {token}

Response (200 OK):

{
  "files": [
    {
      "id": 1,
      "file_type": "zip",
      "original_filename": "work_59027.zip",
      "file_size": 125669,
      "uploaded_at": "2025-12-30T04:56:40.728Z"
    }
  ]
}

📊 BOQ Data

GET /tenders/:tenderId/boq

Description: Get BOQ rows for a tender

Headers: Authorization: Bearer {token}

Query Parameters:

Parameter Type Description
pipe_only boolean Filter to show only RCC pipe items (true/false)
unmapped_only boolean Show only unmapped items (true/false)
limit number Results per page (default: 100)
offset number Pagination offset (default: 0)

Response (200 OK):

{
  "rows": [
    {
      "id": 292,
      "tender_id": 1,
      "file_id": 1,
      "sheet_name": "BoQ1",
      "row_number": 35,
      "description_text": "Providing RCC pipe NP3 1000mm dia...",
      "quantity": "22.5000",
      "unit": "Rm",
      "is_pipe_candidate": true,
      "detected_attributes": {
        "diameter_mm": 1000,
        "class": "NP3",
        "joint_type": "Collar Joint",
        "bedding_type": "Granular"
      },
      "is_mapped": false,
      "created_at": "2025-12-30T05:14:39.356Z"
    }
  ],
  "total": 4,
  "limit": 100,
  "offset": 0
}

Detected Attributes:

Field Type Description
diameter_mm number Pipe diameter in millimeters
class string Pipe class (NP2, NP3, NP4, P1, P2, P3)
joint_type string Joint type (Flush Joint, Collar Joint, Spigot-Socket)
bedding_type string Bedding material (Granular, Concrete)

GET /tenders/boq-row/:rowId

Description: Get single BOQ row by ID

Headers: Authorization: Bearer {token}

Response (200 OK): BOQ row object with full details

Errors: 404 - Row not found

⚠️ Error Handling

Standard Error Response Format

{
  "error": "Error message description"
}

HTTP Status Codes

Code Meaning Common Causes
200 OK Request successful
201 Created Resource created successfully
400 Bad Request Invalid request data, validation failed
401 Unauthorized Missing or invalid authentication token
403 Forbidden Token expired, insufficient permissions
404 Not Found Resource doesn't exist
409 Conflict Duplicate resource (tender number, file checksum)
413 Payload Too Large File exceeds 50MB limit
500 Internal Server Error Server-side error, check logs

🔧 Code Examples

JavaScript (Fetch API)

// Login
const loginResponse = await fetch('https://tenders.ramram.in/api/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: '[email protected]',
    password: 'admin123'
  })
});
const { token } = await loginResponse.json();

// Get tenders
const tendersResponse = await fetch('https://tenders.ramram.in/api/tenders', {
  headers: { 'Authorization': `Bearer ${token}` }
});
const { tenders } = await tendersResponse.json();

// Upload file
const formData = new FormData();
formData.append('file', fileInput.files[0]);

const uploadResponse = await fetch('https://tenders.ramram.in/api/tenders/1/upload', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}` },
  body: formData
});
const fileData = await uploadResponse.json();

cURL

# Login
curl -X POST https://tenders.ramram.in/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"admin123"}'

# Get pipe-only BOQ rows
curl "https://tenders.ramram.in/api/tenders/1/boq?pipe_only=true" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"

# Upload file
curl -X POST https://tenders.ramram.in/api/tenders/1/upload \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -F "file=@work_59027.zip"

📞 Support

Technical Support:
System: tenders.ramram.in
Port: 3001
Contact: Harmony Infotech

API Documentation v1.0 | Tender Management System | © 2025