# Document API Documentation ## Overview The Document API allows authenticated users to generate documents asynchronously (PDF, Excel, etc.) from provided data. The system uses a queue-based architecture for reliable, scalable document generation. **Key Features:** - ✅ Non-blocking requests (returns immediately with jobId) - ✅ Job status polling to track progress - ✅ Automatic retries with exponential backoff - ✅ Support for multiple document types and formats - ✅ Concurrent document generation - ✅ Case-insensitive document names with whitespace and punctuation normalization --- ## Architecture ``` Client Request → Queue Job → Return jobId (202) ↓ Worker processes ↓ Client polls status ↓ Job complete → Download file ``` --- ## Endpoints ### 1. Generate Document **Endpoint:** `POST /api/document/generate` **URL:** `http://localhost:3070/api/document/generate` **Authentication:** Required ✓ **Authorization:** Required - User must have one of the following roles: - `admin` - `management` - `team_head` - `user` **HTTP Status:** `202 Accepted` #### Request Headers ``` Content-Type: application/json Authorization: Bearer ``` #### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `document` | string | Yes | Type of document (e.g., "INVOICE", "DISPATCHNOTE", "DELIVERYNOTE") | | `documentType` | string | Yes | Output format (e.g., "pdf", "excel") | | `documentData` | object | Yes | Data object for the document | #### Response (202 Accepted) ```json { "success": true, "message": "Document generation started", "jobId": "1" } ``` #### Error Responses **400 Bad Request:** ```json { "success": false, "message": "document, documentType, and documentData are required" } ``` **500 Server Error:** ```json { "success": false, "message": "Error message describing the issue" } ``` ### Supported Document Values You can send the document name in any case. Spaces, underscores, and hyphens are ignored by the generator. - `PRECOST` - `INVOICE` - `DISPATCHNOTE` - `DELIVERYNOTE` - `CUSTOMDOCUMENT` - `CREDITNOTE` - `PO` ## Sample Request Bodies All examples below are for `POST /api/document/generate` with `documentType: "pdf"`. ### PreCost ```json { "document": "PRECOST", "documentType": "pdf", "documentData": { "vessel_name": "MV OCEAN STAR", "supplyPort": "DUBAI", "clientRfqNum": "RFQ-2026-001", "omsRfqNum": "OMS-RFQ-0001", "date": "08/07/2026", "items": [ { "name": "Marine Rope", "description": "High strength rope", "qty": 10, "unitPrice": 12.5 }, { "name": "Safety Gloves", "description": "Blue gloves", "qty": 5, "unitPrice": 8 } ], "sub_total": 125, "discount": 0, "additionalCharges": 0, "total_cost": 125 } } ``` ### Invoice ```json { "document": "INVOICE", "documentType": "pdf", "documentData": { "jobReference": "JOB-2026-001", "poNumber": "PO-20254161", "date": "08/07/2026", "pageLabel": "Page 01 of 02", "billToName": "SUNRICH SHIP CHANDLERS L.L.C", "billToAddress": "BUSINESS BAY - ASPECT TOWER, 22ND FLOOR, OFFICE NO. 2201, P.O. BOX NO 39976, DUBAI", "vesselName": "MV OCEAN STAR", "purposeOfCall": "SUPPLY OF PROVISIONS", "supplyDate": "08/07/2026", "grt": "12345", "imoNumber": "9876543", "portCountry": "DUBAI / UAE", "items": [ { "description": "Marine rope", "remarks": "Delivered as requested", "quantity": 10, "unit_price": 12.5 }, { "description": "Safety gloves", "remarks": "Blue color", "quantity": 5, "unit_price": 8 } ], "subtotal": 165, "tax": 0, "discount": 0, "total": 165, "paymentTerms": "Payment due within 30 days", "notes": "Please verify quantities upon delivery." } } ``` ### Dispatch Note ```json { "document": "DISPATCHNOTE", "documentType": "pdf", "documentData": { "referenceNumber": "DN/2026/001", "date": "08/07/2026", "vessel": "MV OCEAN STAR", "captain": "CAPT. JOHN DOE", "cook": "SAMUEL", "agent": "OCEANIC AGENT LTD", "details": "Delivery of provisions and stores", "placeOfDelivery": "DUBAI PORT", "eta": "08/07/2026 08:00", "etd": "08/07/2026 18:00", "numberOfCrew": 18, "nextMainOrderIn": "24 HRS", "port": "DUBAI", "company": "OCEANIC SHIP CHANDLERS (PVT) LTD", "fileNo": "FILE-2026-04", "items": [ { "product": "Rice", "qty": 10, "unit": "BAGS", "supp": "OMS", "poNo": "PO-001", "receivedQty": 10, "issuedQty": 10, "expDate": "12/07/2026", "receivedTime": "09:15", "date": "08/07/2026", "rejects": "", "fatCon": "", "remark": "Delivered" } ], "showSignatures": true, "preparedBy": "Admin User", "approvedBy": "Manager User" } } ``` ### Delivery Note ```json { "document": "DELIVERYNOTE", "documentType": "pdf", "documentData": { "referenceNumber": "DN/OSC/2602/01", "date": "08/07/2026", "billToName": "SUNRICH SHIP CHANDLERS L.L.C", "billToAddress": "BUSINESS BAY - ASPECT TOWER, 22ND FLOOR, OFFICE NO. 2201, P.O. BOX NO 39976, DUBAI", "supplyDate": "08/07/2026", "poNumber": "PO-20254161", "items": [ { "description": "Marine rope", "remarks": "Delivered as requested", "unit": "PCS", "quantity": 10 }, { "description": "Safety gloves", "remarks": "Blue color", "unit": "BOX", "quantity": 5 } ] } } ``` ### Custom Document ```json { "document": "CUSTOMDOCUMENT", "documentType": "pdf", "documentData": { "title": "CUSTOM DOCUMENT", "date": "08/07/2026", "telePhone": "+94 112 083 206", "emailAddress": "shipsupply@oceanicmsol.com", "officeAddressLine1": "Level 5D, Valiant Towers, Nawala Mawatha", "officeAddressLine2": "Colombo 02", "officeAddressLine3": "SRI LANKA", "directorOfCustoms": "The Director of Customs", "vehicleNo": "WPLO 9767", "permitNo": "MV20231214006001 / Permit No. MP20240717017006", "chiefSecurityOfficer": "Chief Security Officer", "exportGate": "Export Gate", "slpa": "SLPA", "gateNo": "Gate No. 03", "permissionText": "Please grant permission to pass these Customs entry papers to supply goods to the vessel", "companyName": "Oceanic Maritime Solutions (Pvt) Ltd", "subtitle": "LICENSED SHIPCHANDLERS", "sectionCode": "SC/FORM/06", "items": [ { "quantity": 10, "unit": "PCS", "description": "Marine rope", "rate": 12.5, "total": 125, "confirmOrderRate": "" }, { "quantity": 5, "unit": "PCS", "description": "Safety gloves", "rate": 8, "total": 40, "confirmOrderRate": "" } ] } } ``` ### Credit Note ```json { "document": "CREDITNOTE", "documentType": "pdf", "documentData": { "companyName": "GREEK-LANKA MARITIME SERVICES (PVT) LTD", "companyAddressLine1": "No. 56/2, Dharmapala Mw, Kotte", "companyAddressLine2": "Sri Jayewardenepura", "companyAddressLine3": "Sri Lanka", "companyPostal": "Postal Code : 10100", "companyContact": "Tel:+94 11 2083206 / Mobile (24/7) : +94 777 232 271", "companyBR": "BR No : PV 0022630", "companyLicense": "License No : SA00317-2024", "crnNo": "GLMS/COLOMBO/474/CRN01", "date": "26/07/2024", "billToName": "Master & Owner of MV TEAM VENTURES", "billToDetails": "VRIDHI MARITIME SHIP MANAGEMENT & OPERATION LLC\nOffice No: 2004, The Prism Tower, Dubai\n20th Floor, Business Bay,\nDubai - PO Box 29583.", "vesselName": "MV \"TEAM VENTURES\"", "grt": "25,543 MT", "port": "REPAIRS AT COLOMBO DOCK YARD", "imoNumber": "9339765", "nameOfAgent": "29/04/2024 AT 21:00 HRS.LT", "portCountry": "COLOMBO / SRI LANKA", "items": [ { "description": "SIGN OFF C/E MR. DHANSINGH BHAURYAL AND M/S J/E MR. SENTHIL", "amount": 50 }, { "description": "VISA + Handling Charges - M/S M/S MR. SENTHIL", "amount": 100 }, { "description": "Transport from Airport to Dockyard", "amount": 70 } ], "totalAmount": 220, "approvedBy": "GLMS Finance Dept.", "approvedByLabel": "Approved by GLMS Finance Dept.", "departmentLabel": "GLMS - Accounts Department" } } ``` } ``` --- ### 2. Get Job Status **Endpoint:** `GET /api/document/job/:jobId/status` **URL:** `http://localhost:3070/api/document/job/1/status` **Authentication:** Required ✓ **Authorization:** Required **HTTP Status:** `200 OK` #### Request Headers ``` Authorization: Bearer ``` #### Response (Waiting/Active) ```json { "success": true, "jobId": "1", "state": "waiting", "result": null, "error": null, "attempts": 0, "stacktrace": [] } ``` #### Response (Completed) ```json { "success": true, "jobId": "83", "state": "completed", "result": { "fileName": "precost-1781614132061.pdf", "mimeType": "application/pdf", "size": 220008, "s3Key": "uploads/16adf29b-c0c2-45f9-8808-d6e15cd3d755.pdf", "documentId": "16adf29b-c0c2-45f9-8808-d6e15cd3d755" }, "error": null, "attempts": 1, "stacktrace": [] } ``` #### Response (Failed) ```json { "success": true, "jobId": "1", "state": "failed", "result": null, "error": "Error message explaining the failure", "attempts": 3, "stacktrace": ["stack trace line 1", "stack trace line 2"] } ``` #### Job States | State | Meaning | Next State | |-------|---------|-----------| | `waiting` | Job queued, waiting for worker | `active` | | `active` | Worker currently processing | `completed` or `failed` | | `completed` | Job done, result available | (final) | | `failed` | Job failed after 3 retries | (final) | | `delayed` | Scheduled for later processing | `waiting` | #### Error Responses **404 Not Found:** ```json { "success": false, "message": "Job not found" } ``` --- ### 3. Download Document **Endpoint:** `GET /api/document/download/:uuid` **URL:** `http://localhost:3070/api/document/download/16adf29b-c0c2-45f9-8808-d6e15cd3d755` **Authentication:** Required ✓ **Authorization:** Required **HTTP Status:** `200 OK` #### Request Headers ``` Authorization: Bearer ``` #### Response (Success) Returns the binary file with appropriate headers: | Header | Value | |--------|-------| | `Content-Type` | `application/pdf` or `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | | `Content-Disposition` | `attachment; filename="[fileName]"` | **Response Body:** Binary file content #### Error Responses **400 Bad Request:** ```json { "success": false, "message": "fileName is required" } ``` **404 Not Found:** ```json { "success": false, "message": "Document not found" } ``` --- ### 4. Cancel Job **Endpoint:** `DELETE /api/document/job/:jobId` **URL:** `http://localhost:3070/api/document/job/1` **Authentication:** Required ✓ **Authorization:** Required **HTTP Status:** `200 OK` #### Request Headers ``` Authorization: Bearer ``` #### Response (Success) ```json { "success": true, "message": "Job cancelled successfully", "jobId": "1" } ``` #### Error Responses **404 Not Found:** ```json { "success": false, "message": "Job not found" } ``` --- ## Document Data Notes The generator is tolerant of different request shapes and common field aliases. For example, it accepts both `documentData` and nested `data`, and it normalizes document names by removing spaces, underscores, and hyphens. If a document does not use every field in a sample payload, you can omit the unused keys. --- ## Complete Workflow Example ### 1. Generate Document ```bash curl -X POST http://localhost:3070/api/document/generate \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -d '{ "document": "precost", "documentType": "pdf", "documentData": { "customerName": "Acme Corp", "items": [ {"name": "Item 1", "description": "Test", "qty": 2, "unitPrice": 100} ] } }' ``` **Response:** ```json { "success": true, "message": "Document generation started", "jobId": "1" } ``` ### 2. Check Job Status ```bash curl http://localhost:3070/api/document/job/1/status \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` **Polling Response (Still Processing):** ```json { "success": true, "jobId": "1", "state": "active" } ``` **Polling Response (Completed):** ```json { "success": true, "jobId": "1", "state": "completed", "result": { "fileName": "precost-1715339340000.pdf" } } ``` ### 3. Download File ```bash curl http://localhost:3070/api/document/download/16adf29b-c0c2-45f9-8808-d6e15cd3d755 \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -o my-document.pdf ``` --- ## JavaScript/Fetch Example ```javascript async function generateAndDownloadDocument(token) { // Step 1: Generate document const generateRes = await fetch("/api/document/generate", { method: "POST", headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }, body: JSON.stringify({ document: "precost", documentType: "pdf", documentData: { customerName: "Acme Corp", items: [ { name: "Item 1", description: "Test", qty: 2, unitPrice: 100 } ] } }) }); const { jobId } = await generateRes.json(); console.log("Job queued:", jobId); // Step 2: Poll for completion let isComplete = false; let result = null; while (!isComplete) { const statusRes = await fetch(`/api/document/job/${jobId}/status`, { headers: { "Authorization": `Bearer ${token}` } }); const status = await statusRes.json(); console.log("Job state:", status.state); if (status.state === "completed") { result = status.result; isComplete = true; } else if (status.state === "failed") { throw new Error(`Job failed: ${status.error}`); } else { // Wait 2 seconds before polling again await new Promise(r => setTimeout(r, 2000)); } } // Step 3: Download the file const downloadRes = await fetch(`/api/document/download/${result.fileName}`, { headers: { "Authorization": `Bearer ${token}` } }); const blob = await downloadRes.blob(); // Trigger browser download const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = result.fileName; a.click(); window.URL.revokeObjectURL(url); console.log("Download complete!"); } ``` --- ## Important Notes ### Authentication & Authorization - All endpoints require JWT token in `Authorization: Bearer ` header - User must have one of these roles: `admin`, `management`, `team_head`, `user` ### Polling Strategy - Poll status every 2-5 seconds - Max recommended: 60 attempts (5 minutes timeout) - Move to download immediately when state is `completed` ### Error Handling - Jobs automatically retry up to 3 times with exponential backoff - Failed jobs remain in queue for debugging - Check `error` and `stacktrace` fields for failure details ### File Management - Generated files stored in `/app/documents/` - File names auto-generated: `{document}-{timestamp}.{ext}` - Always sanitize fileName in download requests ### Supported Formats - **PDF:** `documentType: "pdf"` - **Excel:** `documentType: "excel"` ### Performance - Worker concurrency: 2 concurrent jobs - Timeout per job: 5 minutes - Retry attempts: 3 with exponential backoff --- ## Common Response Codes | Code | Meaning | |------|---------| | `202` | Accepted - Job queued successfully | | `200` | OK - Successful operation | | `400` | Bad Request - Invalid input | | `401` | Unauthorized - Missing/invalid token | | `403` | Forbidden - Insufficient permissions | | `404` | Not Found - Job or file not found | | `500` | Server Error - Internal error | --- ## Migration Notes **If upgrading from synchronous API:** **Old (Synchronous):** - Request returned file immediately - Long request timeout - Blocking operation **New (Asynchronous):** - Request returns jobId immediately (202) - Poll `/api/document/job/{jobId}/status` for progress - Non-blocking, scalable architecture - Automatic retries built-in