openapi: 3.0.3 info: title: Goa GEL - Blockchain Document Verification Platform API description: | ## Overview REST API for the Government of Goa's Blockchain-based Document Verification Platform (GEL). This platform enables multi-department approval workflows for various licenses and permits, with blockchain-backed verification using Hyperledger Besu and ERC-721 Soulbound NFTs. ## Authentication - **Department APIs**: Use `X-API-Key` and `X-Department-Code` headers - **Applicant APIs**: Use Bearer token from DigiLocker authentication (mock for POC) - **Admin APIs**: Use Bearer token with admin role ## Blockchain Integration All critical operations (request creation, approvals, document updates) are recorded on-chain. Response includes `transactionHash` for blockchain verification. ## Webhooks Departments can register webhooks to receive real-time notifications for: - `APPROVAL_REQUIRED` - New request pending approval - `DOCUMENT_UPDATED` - Applicant updated a document - `REQUEST_APPROVED` - Request fully approved - `REQUEST_REJECTED` - Request rejected version: 1.0.0 contact: name: Goa GEL Platform Support email: support@goagel.gov.in license: name: Government of Goa url: https://www.goa.gov.in servers: - url: https://api.goagel.gov.in/api/v1 description: Production server - url: https://staging-api.goagel.gov.in/api/v1 description: Staging server - url: http://localhost:3001/api/v1 description: Local development tags: - name: Requests description: License request operations - name: Documents description: Document upload and retrieval - name: Approvals description: Department approval actions - name: Departments description: Department management - name: Workflows description: Workflow configuration - name: Webhooks description: Webhook management - name: Admin description: Platform administration - name: Verification description: Public verification endpoints paths: # ==================== REQUESTS ==================== /requests: post: tags: - Requests summary: Create new license request description: | Creates a new license request and mints a draft NFT on the blockchain. The request starts in DRAFT status until submitted. operationId: createRequest security: - BearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateRequestInput' example: applicantId: "DL-GOA-123456789" requestType: "RESORT_LICENSE" metadata: resortName: "Paradise Beach Resort" location: "Calangute, North Goa" plotArea: 5000 builtUpArea: 3500 numberOfRooms: 50 responses: '201': description: Request created successfully content: application/json: schema: $ref: '#/components/schemas/RequestResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' get: tags: - Requests summary: List requests description: Get list of requests with optional filters operationId: listRequests security: - BearerAuth: [] - ApiKeyAuth: [] parameters: - name: status in: query schema: $ref: '#/components/schemas/RequestStatus' - name: requestType in: query schema: type: string example: "RESORT_LICENSE" - name: applicantId in: query schema: type: string - name: page in: query schema: type: integer default: 1 minimum: 1 - name: limit in: query schema: type: integer default: 20 minimum: 1 maximum: 100 - name: sortBy in: query schema: type: string enum: [createdAt, updatedAt, status] default: createdAt - name: sortOrder in: query schema: type: string enum: [asc, desc] default: desc responses: '200': description: List of requests content: application/json: schema: $ref: '#/components/schemas/RequestListResponse' /requests/pending: get: tags: - Requests summary: Get requests pending for department description: Returns all requests that are pending approval from the specified department operationId: getPendingRequests security: - ApiKeyAuth: [] parameters: - name: department in: query required: true schema: type: string example: "FIRE_DEPT" - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 20 responses: '200': description: List of pending requests content: application/json: schema: $ref: '#/components/schemas/RequestListResponse' /requests/{requestId}: get: tags: - Requests summary: Get request details description: | Returns complete request details including documents, approvals, current workflow stage, and full timeline of events. operationId: getRequest security: - BearerAuth: [] - ApiKeyAuth: [] parameters: - $ref: '#/components/parameters/RequestId' responses: '200': description: Request details content: application/json: schema: $ref: '#/components/schemas/RequestDetailResponse' '404': $ref: '#/components/responses/NotFound' /requests/{requestId}/submit: post: tags: - Requests summary: Submit request for approval description: | Submits the request for departmental approval workflow. Validates that all required documents are uploaded before submission. Records submission on blockchain and notifies relevant departments. operationId: submitRequest security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/RequestId' responses: '200': description: Request submitted successfully content: application/json: schema: type: object properties: requestId: type: string format: uuid status: type: string example: "SUBMITTED" currentStage: $ref: '#/components/schemas/WorkflowStage' pendingDepartments: type: array items: type: string example: ["FIRE_DEPT", "TOURISM_DEPT"] transactionHash: type: string example: "0x1234567890abcdef..." '400': description: Missing required documents content: application/json: schema: $ref: '#/components/schemas/Error' example: code: "MISSING_DOCUMENTS" message: "Required documents not uploaded" details: missingDocuments: ["FIRE_SAFETY_CERTIFICATE", "BUILDING_PLAN"] /requests/{requestId}/cancel: post: tags: - Requests summary: Cancel request description: Cancels a request that is in DRAFT or PENDING_RESUBMISSION status operationId: cancelRequest security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/RequestId' requestBody: content: application/json: schema: type: object properties: reason: type: string example: "No longer needed" responses: '200': description: Request cancelled content: application/json: schema: type: object properties: requestId: type: string status: type: string example: "CANCELLED" transactionHash: type: string /requests/{requestId}/timeline: get: tags: - Requests summary: Get request timeline description: Returns chronological list of all events for this request operationId: getRequestTimeline security: - BearerAuth: [] - ApiKeyAuth: [] parameters: - $ref: '#/components/parameters/RequestId' responses: '200': description: Request timeline content: application/json: schema: type: object properties: requestId: type: string events: type: array items: $ref: '#/components/schemas/TimelineEvent' # ==================== DOCUMENTS ==================== /requests/{requestId}/documents: post: tags: - Documents summary: Upload document description: | Uploads a document for the request. The document is: 1. Stored in MinIO with versioning 2. Hash generated using SHA-256 3. Hash recorded on blockchain 4. Linked to the request NFT operationId: uploadDocument security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/RequestId' requestBody: required: true content: multipart/form-data: schema: type: object required: - file - docType properties: file: type: string format: binary description: Document file (PDF, JPG, PNG) docType: type: string description: Document type code example: "FIRE_SAFETY_CERTIFICATE" description: type: string description: Optional description example: "Fire safety certificate from Fire Department" responses: '201': description: Document uploaded successfully content: application/json: schema: $ref: '#/components/schemas/DocumentResponse' '400': description: Invalid file or document type '413': description: File too large (max 10MB) get: tags: - Documents summary: List request documents description: Returns all documents associated with the request operationId: listRequestDocuments security: - BearerAuth: [] - ApiKeyAuth: [] parameters: - $ref: '#/components/parameters/RequestId' responses: '200': description: List of documents content: application/json: schema: type: object properties: requestId: type: string documents: type: array items: $ref: '#/components/schemas/Document' /documents/{documentId}: get: tags: - Documents summary: Get document details description: Returns document metadata and all versions operationId: getDocument security: - BearerAuth: [] - ApiKeyAuth: [] parameters: - name: documentId in: path required: true schema: type: string format: uuid responses: '200': description: Document details content: application/json: schema: $ref: '#/components/schemas/DocumentDetailResponse' /documents/{documentId}/download: get: tags: - Documents summary: Download document description: Returns a signed URL for downloading the document operationId: downloadDocument security: - BearerAuth: [] - ApiKeyAuth: [] parameters: - name: documentId in: path required: true schema: type: string format: uuid - name: version in: query description: Specific version to download (defaults to latest) schema: type: integer responses: '200': description: Download URL content: application/json: schema: type: object properties: downloadUrl: type: string format: uri description: Signed URL (expires in 1 hour) expiresAt: type: string format: date-time /requests/{requestId}/documents/{documentId}: put: tags: - Documents summary: Update document (new version) description: | Uploads a new version of an existing document. This will: 1. Create new version in MinIO 2. Generate new hash 3. Record new hash on blockchain 4. Mark affected approvals as "REVIEW_REQUIRED" 5. Notify affected departments via webhook operationId: updateDocument security: - BearerAuth: [] parameters: - $ref: '#/components/parameters/RequestId' - name: documentId in: path required: true schema: type: string format: uuid requestBody: required: true content: multipart/form-data: schema: type: object required: - file properties: file: type: string format: binary description: type: string responses: '200': description: Document updated content: application/json: schema: type: object properties: documentId: type: string newVersion: type: integer newHash: type: string invalidatedApprovals: type: array items: type: string description: Department codes that need re-review transactionHash: type: string # ==================== APPROVALS ==================== /requests/{requestId}/approve: post: tags: - Approvals summary: Approve request description: | Records department approval on the blockchain. If all required approvals for the current stage are complete, automatically advances to the next stage or finalizes the request. operationId: approveRequest security: - ApiKeyAuth: [] parameters: - $ref: '#/components/parameters/RequestId' requestBody: required: true content: application/json: schema: type: object required: - remarks - reviewedDocuments properties: remarks: type: string example: "All fire safety requirements met" reviewedDocuments: type: array items: type: string format: uuid description: Document IDs that were reviewed responses: '200': description: Approval recorded content: application/json: schema: $ref: '#/components/schemas/ApprovalResponse' /requests/{requestId}/reject: post: tags: - Approvals summary: Reject request description: Records department rejection. This permanently rejects the request. operationId: rejectRequest security: - ApiKeyAuth: [] parameters: - $ref: '#/components/parameters/RequestId' requestBody: required: true content: application/json: schema: type: object required: - remarks - reason properties: remarks: type: string example: "Fire safety standards not met" reason: type: string enum: - SAFETY_VIOLATION - INCOMPLETE_DOCUMENTS - POLICY_VIOLATION - FRAUDULENT_APPLICATION - OTHER responses: '200': description: Rejection recorded content: application/json: schema: $ref: '#/components/schemas/ApprovalResponse' /requests/{requestId}/request-changes: post: tags: - Approvals summary: Request changes description: | Requests additional information or document updates from the applicant. The request status changes to PENDING_RESUBMISSION. operationId: requestChanges security: - ApiKeyAuth: [] parameters: - $ref: '#/components/parameters/RequestId' requestBody: required: true content: application/json: schema: type: object required: - remarks properties: remarks: type: string example: "Please provide updated fire safety certificate with recent inspection" requiredDocuments: type: array items: type: string description: Additional document types needed example: ["FIRE_SAFETY_CERTIFICATE", "INSPECTION_REPORT"] responses: '200': description: Changes requested content: application/json: schema: type: object properties: status: type: string example: "CHANGES_REQUESTED" transactionHash: type: string /requests/{requestId}/approvals: get: tags: - Approvals summary: Get all approvals for request description: Returns all approval records including historical/invalidated ones operationId: getRequestApprovals security: - BearerAuth: [] - ApiKeyAuth: [] parameters: - $ref: '#/components/parameters/RequestId' - name: includeInvalidated in: query schema: type: boolean default: false responses: '200': description: List of approvals content: application/json: schema: type: object properties: requestId: type: string approvals: type: array items: $ref: '#/components/schemas/Approval' # ==================== DEPARTMENTS ==================== /departments: get: tags: - Departments summary: List all departments description: Returns all registered departments operationId: listDepartments security: - BearerAuth: [] - ApiKeyAuth: [] responses: '200': description: List of departments content: application/json: schema: type: object properties: departments: type: array items: $ref: '#/components/schemas/Department' post: tags: - Departments summary: Register new department description: Registers a new department and creates blockchain wallet operationId: createDepartment security: - AdminAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateDepartmentInput' responses: '201': description: Department created content: application/json: schema: $ref: '#/components/schemas/DepartmentResponse' /departments/{departmentCode}: get: tags: - Departments summary: Get department details operationId: getDepartment security: - BearerAuth: [] - ApiKeyAuth: [] parameters: - name: departmentCode in: path required: true schema: type: string example: "FIRE_DEPT" responses: '200': description: Department details content: application/json: schema: $ref: '#/components/schemas/Department' patch: tags: - Departments summary: Update department operationId: updateDepartment security: - AdminAuth: [] parameters: - name: departmentCode in: path required: true schema: type: string requestBody: content: application/json: schema: type: object properties: name: type: string webhookUrl: type: string format: uri isActive: type: boolean responses: '200': description: Department updated content: application/json: schema: $ref: '#/components/schemas/Department' /departments/{departmentCode}/regenerate-api-key: post: tags: - Departments summary: Regenerate API key description: Generates a new API key for the department (invalidates the old key) operationId: regenerateApiKey security: - AdminAuth: [] parameters: - name: departmentCode in: path required: true schema: type: string responses: '200': description: New API key generated content: application/json: schema: type: object properties: apiKey: type: string description: New API key (shown only once) apiSecret: type: string description: New API secret (shown only once) /departments/{departmentCode}/stats: get: tags: - Departments summary: Get department statistics operationId: getDepartmentStats security: - ApiKeyAuth: [] - AdminAuth: [] parameters: - name: departmentCode in: path required: true schema: type: string - name: startDate in: query schema: type: string format: date - name: endDate in: query schema: type: string format: date responses: '200': description: Department statistics content: application/json: schema: type: object properties: departmentCode: type: string period: type: object properties: start: type: string format: date end: type: string format: date stats: type: object properties: totalReceived: type: integer approved: type: integer rejected: type: integer pending: type: integer avgProcessingTimeDays: type: number # ==================== WORKFLOWS ==================== /workflows: get: tags: - Workflows summary: List workflow definitions operationId: listWorkflows security: - AdminAuth: [] parameters: - name: isActive in: query schema: type: boolean responses: '200': description: List of workflows content: application/json: schema: type: object properties: workflows: type: array items: $ref: '#/components/schemas/Workflow' post: tags: - Workflows summary: Create workflow definition operationId: createWorkflow security: - AdminAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateWorkflowInput' responses: '201': description: Workflow created content: application/json: schema: $ref: '#/components/schemas/Workflow' /workflows/{workflowId}: get: tags: - Workflows summary: Get workflow details operationId: getWorkflow security: - AdminAuth: [] parameters: - name: workflowId in: path required: true schema: type: string format: uuid responses: '200': description: Workflow details content: application/json: schema: $ref: '#/components/schemas/Workflow' put: tags: - Workflows summary: Update workflow description: | Updates a workflow definition. Creates a new version. In-progress requests continue with their original workflow version. operationId: updateWorkflow security: - AdminAuth: [] parameters: - name: workflowId in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateWorkflowInput' responses: '200': description: Workflow updated content: application/json: schema: $ref: '#/components/schemas/Workflow' delete: tags: - Workflows summary: Deactivate workflow description: Deactivates the workflow. Cannot be used for new requests. operationId: deactivateWorkflow security: - AdminAuth: [] parameters: - name: workflowId in: path required: true schema: type: string format: uuid responses: '200': description: Workflow deactivated /workflows/{workflowId}/validate: post: tags: - Workflows summary: Validate workflow definition description: Validates workflow for circular dependencies, missing departments, etc. operationId: validateWorkflow security: - AdminAuth: [] parameters: - name: workflowId in: path required: true schema: type: string format: uuid responses: '200': description: Validation result content: application/json: schema: type: object properties: isValid: type: boolean errors: type: array items: type: object properties: code: type: string message: type: string path: type: string # ==================== WEBHOOKS ==================== /webhooks: get: tags: - Webhooks summary: List registered webhooks operationId: listWebhooks security: - ApiKeyAuth: [] - AdminAuth: [] responses: '200': description: List of webhooks content: application/json: schema: type: object properties: webhooks: type: array items: $ref: '#/components/schemas/Webhook' post: tags: - Webhooks summary: Register webhook operationId: createWebhook security: - ApiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateWebhookInput' responses: '201': description: Webhook registered content: application/json: schema: $ref: '#/components/schemas/Webhook' /webhooks/{webhookId}: delete: tags: - Webhooks summary: Delete webhook operationId: deleteWebhook security: - ApiKeyAuth: [] parameters: - name: webhookId in: path required: true schema: type: string format: uuid responses: '204': description: Webhook deleted /webhooks/{webhookId}/test: post: tags: - Webhooks summary: Test webhook description: Sends a test payload to the webhook URL operationId: testWebhook security: - ApiKeyAuth: [] parameters: - name: webhookId in: path required: true schema: type: string format: uuid responses: '200': description: Test result content: application/json: schema: type: object properties: success: type: boolean statusCode: type: integer responseTime: type: integer description: Response time in ms /webhooks/logs: get: tags: - Webhooks summary: Get webhook delivery logs operationId: getWebhookLogs security: - ApiKeyAuth: [] - AdminAuth: [] parameters: - name: webhookId in: query schema: type: string format: uuid - name: status in: query schema: type: string enum: [success, failed, pending] - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 50 responses: '200': description: Webhook logs content: application/json: schema: type: object properties: logs: type: array items: $ref: '#/components/schemas/WebhookLog' # ==================== ADMIN ==================== /admin/stats: get: tags: - Admin summary: Get platform statistics operationId: getPlatformStats security: - AdminAuth: [] parameters: - name: startDate in: query schema: type: string format: date - name: endDate in: query schema: type: string format: date responses: '200': description: Platform statistics content: application/json: schema: type: object properties: period: type: object properties: start: type: string format: date end: type: string format: date requests: type: object properties: total: type: integer byStatus: type: object additionalProperties: type: integer byType: type: object additionalProperties: type: integer blockchain: type: object properties: totalTransactions: type: integer nftsMinted: type: integer avgGasUsed: type: number performance: type: object properties: avgProcessingTimeDays: type: number requestsPerDay: type: number /admin/audit-logs: get: tags: - Admin summary: Get audit logs operationId: getAuditLogs security: - AdminAuth: [] parameters: - name: entityType in: query schema: type: string enum: [REQUEST, APPROVAL, DOCUMENT, DEPARTMENT, WORKFLOW] - name: entityId in: query schema: type: string format: uuid - name: action in: query schema: type: string - name: actorId in: query schema: type: string format: uuid - name: startDate in: query schema: type: string format: date-time - name: endDate in: query schema: type: string format: date-time - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 50 responses: '200': description: Audit logs content: application/json: schema: type: object properties: logs: type: array items: $ref: '#/components/schemas/AuditLog' pagination: $ref: '#/components/schemas/Pagination' /admin/blockchain/status: get: tags: - Admin summary: Get blockchain network status operationId: getBlockchainStatus security: - AdminAuth: [] responses: '200': description: Blockchain status content: application/json: schema: type: object properties: network: type: object properties: chainId: type: integer networkId: type: integer consensus: type: string example: "QBFT" nodes: type: array items: type: object properties: nodeId: type: string isValidator: type: boolean isHealthy: type: boolean peers: type: integer latestBlock: type: integer latestBlock: type: object properties: number: type: integer hash: type: string timestamp: type: string format: date-time /admin/blockchain/transactions: get: tags: - Admin summary: Get blockchain transactions operationId: getBlockchainTransactions security: - AdminAuth: [] parameters: - name: txType in: query schema: type: string enum: [MINT_NFT, APPROVAL, DOC_UPDATE, REJECT, REVOKE] - name: status in: query schema: type: string enum: [PENDING, CONFIRMED, FAILED] - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 50 responses: '200': description: Blockchain transactions content: application/json: schema: type: object properties: transactions: type: array items: $ref: '#/components/schemas/BlockchainTransaction' pagination: $ref: '#/components/schemas/Pagination' # ==================== VERIFICATION ==================== /verify/{tokenId}: get: tags: - Verification summary: Verify license by token ID description: | Public endpoint for verifying a license using its NFT token ID. No authentication required. operationId: verifyByTokenId parameters: - name: tokenId in: path required: true schema: type: integer example: 12345 responses: '200': description: Verification result content: application/json: schema: $ref: '#/components/schemas/VerificationResponse' '404': description: Token not found /verify/qr/{qrCode}: get: tags: - Verification summary: Verify license by QR code description: Public endpoint for verifying a license using QR code data operationId: verifyByQrCode parameters: - name: qrCode in: path required: true schema: type: string responses: '200': description: Verification result content: application/json: schema: $ref: '#/components/schemas/VerificationResponse' /verify/document/{hash}: get: tags: - Verification summary: Verify document by hash description: Public endpoint for verifying a document using its hash operationId: verifyDocumentByHash parameters: - name: hash in: path required: true schema: type: string example: "0x1234567890abcdef..." responses: '200': description: Document verification result content: application/json: schema: type: object properties: isValid: type: boolean documentId: type: string requestId: type: string docType: type: string version: type: integer uploadedAt: type: string format: date-time blockchainRecord: type: object properties: transactionHash: type: string blockNumber: type: integer timestamp: type: string format: date-time # ==================== COMPONENTS ==================== components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT token from DigiLocker authentication ApiKeyAuth: type: apiKey in: header name: X-API-Key description: | Department API key. Must be used with X-Department-Code header. AdminAuth: type: http scheme: bearer bearerFormat: JWT description: Admin JWT token parameters: RequestId: name: requestId in: path required: true schema: type: string format: uuid description: Unique request identifier responses: BadRequest: description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error' example: code: "UNAUTHORIZED" message: "Invalid or missing authentication" NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/Error' example: code: "NOT_FOUND" message: "Request not found" schemas: # ===== Input Schemas ===== CreateRequestInput: type: object required: - applicantId - requestType properties: applicantId: type: string description: DigiLocker ID example: "DL-GOA-123456789" requestType: type: string description: Type of license/permit enum: - RESORT_LICENSE - TRADE_LICENSE - BUILDING_PERMIT example: "RESORT_LICENSE" metadata: type: object description: Request-specific data additionalProperties: true CreateDepartmentInput: type: object required: - code - name properties: code: type: string pattern: "^[A-Z_]+$" example: "FIRE_DEPT" name: type: string example: "Fire & Emergency Services Department" webhookUrl: type: string format: uri CreateWorkflowInput: type: object required: - workflowType - name - stages properties: workflowType: type: string example: "RESORT_LICENSE" name: type: string example: "Resort License Approval Workflow" description: type: string stages: type: array items: $ref: '#/components/schemas/WorkflowStageInput' WorkflowStageInput: type: object required: - stageId - stageName - stageOrder - executionType - requiredApprovals properties: stageId: type: string stageName: type: string stageOrder: type: integer executionType: type: string enum: [SEQUENTIAL, PARALLEL] requiredApprovals: type: array items: type: object properties: departmentCode: type: string requiredDocuments: type: array items: type: string isMandatory: type: boolean completionCriteria: type: string enum: [ALL, ANY, THRESHOLD] default: ALL threshold: type: integer timeoutDays: type: integer onTimeout: type: string enum: [NOTIFY, ESCALATE, AUTO_REJECT] onRejection: type: string enum: [FAIL_REQUEST, RETRY_STAGE, ESCALATE] CreateWebhookInput: type: object required: - url - events - secret properties: departmentCode: type: string url: type: string format: uri events: type: array items: type: string enum: - APPROVAL_REQUIRED - DOCUMENT_UPDATED - REQUEST_APPROVED - REQUEST_REJECTED - CHANGES_REQUESTED secret: type: string description: Secret for HMAC signature verification # ===== Response Schemas ===== RequestResponse: type: object properties: requestId: type: string format: uuid requestNumber: type: string example: "RL-2024-001234" status: $ref: '#/components/schemas/RequestStatus' transactionHash: type: string createdAt: type: string format: date-time RequestListResponse: type: object properties: requests: type: array items: $ref: '#/components/schemas/RequestSummary' pagination: $ref: '#/components/schemas/Pagination' RequestSummary: type: object properties: requestId: type: string format: uuid requestNumber: type: string requestType: type: string status: $ref: '#/components/schemas/RequestStatus' applicantName: type: string currentStage: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time RequestDetailResponse: type: object properties: requestId: type: string format: uuid requestNumber: type: string tokenId: type: integer requestType: type: string status: $ref: '#/components/schemas/RequestStatus' applicant: type: object properties: id: type: string name: type: string email: type: string metadata: type: object documents: type: array items: $ref: '#/components/schemas/Document' approvals: type: array items: $ref: '#/components/schemas/Approval' currentStage: $ref: '#/components/schemas/WorkflowStage' timeline: type: array items: $ref: '#/components/schemas/TimelineEvent' blockchainData: type: object properties: tokenId: type: integer contractAddress: type: string creationTxHash: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time submittedAt: type: string format: date-time approvedAt: type: string format: date-time DocumentResponse: type: object properties: documentId: type: string format: uuid docType: type: string hash: type: string version: type: integer transactionHash: type: string createdAt: type: string format: date-time DocumentDetailResponse: type: object properties: documentId: type: string format: uuid docType: type: string originalFilename: type: string currentVersion: type: integer currentHash: type: string versions: type: array items: $ref: '#/components/schemas/DocumentVersion' downloadUrl: type: string format: uri ApprovalResponse: type: object properties: approvalId: type: string format: uuid status: $ref: '#/components/schemas/ApprovalStatus' transactionHash: type: string workflowStatus: type: object properties: currentStage: type: string isComplete: type: boolean nextPendingDepartments: type: array items: type: string DepartmentResponse: type: object properties: code: type: string name: type: string walletAddress: type: string apiKey: type: string description: Shown only on creation apiSecret: type: string description: Shown only on creation isActive: type: boolean createdAt: type: string format: date-time VerificationResponse: type: object properties: isValid: type: boolean tokenId: type: integer requestNumber: type: string requestType: type: string status: $ref: '#/components/schemas/RequestStatus' applicantName: type: string issuedAt: type: string format: date-time expiresAt: type: string format: date-time approvals: type: array items: type: object properties: departmentName: type: string approvedAt: type: string format: date-time blockchainProof: type: object properties: contractAddress: type: string tokenId: type: integer ownerAddress: type: string transactionHash: type: string # ===== Entity Schemas ===== Document: type: object properties: documentId: type: string format: uuid docType: type: string originalFilename: type: string currentVersion: type: integer currentHash: type: string uploadedAt: type: string format: date-time updatedAt: type: string format: date-time DocumentVersion: type: object properties: version: type: integer hash: type: string fileSize: type: integer mimeType: type: string uploadedBy: type: string transactionHash: type: string createdAt: type: string format: date-time Approval: type: object properties: approvalId: type: string format: uuid departmentCode: type: string departmentName: type: string status: $ref: '#/components/schemas/ApprovalStatus' remarks: type: string reviewedDocuments: type: array items: type: string isActive: type: boolean transactionHash: type: string createdAt: type: string format: date-time invalidatedAt: type: string format: date-time invalidationReason: type: string Department: type: object properties: code: type: string name: type: string walletAddress: type: string webhookUrl: type: string isActive: type: boolean createdAt: type: string format: date-time Workflow: type: object properties: workflowId: type: string format: uuid workflowType: type: string name: type: string description: type: string version: type: integer isActive: type: boolean stages: type: array items: $ref: '#/components/schemas/WorkflowStage' createdAt: type: string format: date-time updatedAt: type: string format: date-time WorkflowStage: type: object properties: stageId: type: string stageName: type: string stageOrder: type: integer executionType: type: string enum: [SEQUENTIAL, PARALLEL] requiredApprovals: type: array items: type: object properties: departmentCode: type: string departmentName: type: string requiredDocuments: type: array items: type: string isMandatory: type: boolean completionCriteria: type: string enum: [ALL, ANY, THRESHOLD] threshold: type: integer timeoutDays: type: integer onTimeout: type: string onRejection: type: string Webhook: type: object properties: webhookId: type: string format: uuid departmentCode: type: string url: type: string format: uri events: type: array items: type: string isActive: type: boolean createdAt: type: string format: date-time WebhookLog: type: object properties: logId: type: string format: uuid webhookId: type: string format: uuid eventType: type: string payload: type: object responseStatus: type: integer responseTime: type: integer retryCount: type: integer createdAt: type: string format: date-time TimelineEvent: type: object properties: eventId: type: string format: uuid eventType: type: string enum: - REQUEST_CREATED - REQUEST_SUBMITTED - DOCUMENT_UPLOADED - DOCUMENT_UPDATED - APPROVAL_RECEIVED - REJECTION_RECEIVED - CHANGES_REQUESTED - REQUEST_APPROVED - REQUEST_REJECTED - NFT_MINTED description: type: string actor: type: object properties: type: type: string enum: [APPLICANT, DEPARTMENT, SYSTEM] id: type: string name: type: string metadata: type: object transactionHash: type: string timestamp: type: string format: date-time AuditLog: type: object properties: logId: type: string format: uuid entityType: type: string entityId: type: string format: uuid action: type: string actorType: type: string actorId: type: string format: uuid oldValue: type: object newValue: type: object ipAddress: type: string userAgent: type: string createdAt: type: string format: date-time BlockchainTransaction: type: object properties: txId: type: string format: uuid txHash: type: string txType: type: string enum: [MINT_NFT, APPROVAL, DOC_UPDATE, REJECT, REVOKE] relatedEntityType: type: string relatedEntityId: type: string format: uuid fromAddress: type: string toAddress: type: string status: type: string enum: [PENDING, CONFIRMED, FAILED] blockNumber: type: integer gasUsed: type: integer errorMessage: type: string createdAt: type: string format: date-time confirmedAt: type: string format: date-time # ===== Enums ===== RequestStatus: type: string enum: - DRAFT - SUBMITTED - IN_REVIEW - PENDING_RESUBMISSION - APPROVED - REJECTED - REVOKED - CANCELLED ApprovalStatus: type: string enum: - PENDING - APPROVED - REJECTED - CHANGES_REQUESTED - REVIEW_REQUIRED # ===== Common Schemas ===== Pagination: type: object properties: page: type: integer limit: type: integer total: type: integer totalPages: type: integer hasNext: type: boolean hasPrev: type: boolean Error: type: object properties: code: type: string message: type: string details: type: object timestamp: type: string format: date-time path: type: string