VFS Tracking
VFS (Visa Facilitation Services) tracking is the core operational workflow of Shanvi Travels. It tracks a client’s visa application documents as they move physically through the pipeline — from collection at a consultancy/agent, through Shanvi’s office, to the VFS center, and back to the client.
Each “task” (documents table) represents one applicant’s visa file. A single courier trip can carry multiple tasks simultaneously (grouped as a dispatch batch).
The 8-stage pipeline
Section titled “The 8-stage pipeline”Stage 1 — DOCUMENT_RECEIVER Agent/consultancy uploads applicant documents in the portal ↓Stage 2 — DISPATCHED_TO_SHANVI Courier picks up from agent, in transit to Shanvi office ↓Stage 3 — DOCUMENT_AT_SHANVI Documents received and verified at Shanvi office, submitted to VFS ↓Stage 4 — VFS_RECEIVED VFS center has received the documents ↓Stage 5 — VFS_COLLECTED VFS processing done (visa stamped OR rejected), Shanvi collects from VFS ↓Stage 6 — VFS_AFTER_SHANVI Shanvi hands off to courier, in transit back to agent ↓Stage 7 — CONSULTANCY_RECEIVED Agent/consultancy confirms receipt from courier ↓Stage 8 — CLOSE Agent confirms final delivery to the customer, task closedSpecial statuses:
REJECTED— VFS rejected the visa application. The passport must still return through stages 5→6→7→8. The rejection flag is set at stage 5 but the tracking continues.PENDING— initial state when a document receiver agent creates the task before uploading filesDRAFT— created via admin create form without documents attachedDELETED— soft delete, filtered from default views
Note on duplicate labels: Stages 3 and 5 are both “Document at Shanvi” in the UI, and stages 2 and 6 are both “Document on Way.” The UI labels need to be made distinct — e.g., “At Shanvi (Pre-VFS)” vs “At Shanvi (Post-VFS).” The underlying status strings are already different. This is a frontend display fix only.
Who does what at each stage
Section titled “Who does what at each stage”| Stage | Status | Who updates | Notes |
|---|---|---|---|
| 1 | DOCUMENT_RECEIVER | Agent / Consultancy | Uploads applicant docs via portal |
| 2 | DISPATCHED_TO_SHANVI | Shanvi staff (see Courier section) | Confirms courier has picked up |
| 3 | DOCUMENT_AT_SHANVI | Shanvi office staff | Verifies docs, submits to VFS |
| 4 | VFS_RECEIVED | Shanvi office staff | Confirms VFS receipt |
| 5 | VFS_COLLECTED | Shanvi office staff | Collects from VFS (stamped or rejected) |
| 6 | VFS_AFTER_SHANVI | Shanvi staff (see Courier section) | Hands off to courier for return |
| 7 | CONSULTANCY_RECEIVED | Agent / Consultancy | Confirms they received from courier |
| 8 | CLOSE | Agent / Consultancy | Confirms client received documents |
Courier partner — current approach and upgrade path
Section titled “Courier partner — current approach and upgrade path”The courier physically carries documents between the agent and Shanvi’s office. The system currently has no courier representation — stages 2 and 6 are updated by Shanvi staff on the courier’s behalf.
Confirmed approach — Shanvi staff marks courier stages
Section titled “Confirmed approach — Shanvi staff marks courier stages”Decision (confirmed): Shanvi office staff are responsible for updating stages 2 and 6. No courier portal login or external integration is needed.
- Stage 2 (
DISPATCHED_TO_SHANVI): Shanvi staff marks this when they confirm (by phone) that the courier has picked up the documents from the agent. - Stage 6 (
VFS_AFTER_SHANVI): Shanvi staff marks this when they hand the batch to the courier for return delivery.
The courier’s identity is recorded as free text (courierName) on the dispatch batch — no system account required.
This approach is intentionally simple. If the business grows and a courier audit trail becomes important, the dispatch_batches table already has a nullable courierId FK so a Courier portal login can be added later without a schema migration.
Dispatch batch — grouping multiple applications per courier trip
Section titled “Dispatch batch — grouping multiple applications per courier trip”One courier trip carries multiple applicants’ passports. Currently there is no batch grouping in the system — each document is tracked independently with no link to the courier trip it traveled on.
Proposed dispatch_batches table
Section titled “Proposed dispatch_batches table”CREATE TABLE dispatch_batches ( id INTEGER PRIMARY KEY AUTOINCREMENT, displayId TEXT NOT NULL, -- e.g. "BATCH-20260529-001" direction TEXT NOT NULL, -- "TO_VFS" or "FROM_VFS" courierId INTEGER, -- FK to users — reserved for future courier portal login, NULL for now courierName TEXT NOT NULL, -- free-text courier name (e.g. "Ram Bahadur / Prabhu Courier") handoffDate DATE NOT NULL, notes TEXT, createdBy INTEGER NOT NULL, -- Shanvi staff who created the batch createdAt DATETIME DEFAULT CURRENT_TIMESTAMP);
-- Link table: which documents are in which batchCREATE TABLE batch_documents ( batchId INTEGER NOT NULL REFERENCES dispatch_batches(id), documentId INTEGER NOT NULL REFERENCES documents(id), PRIMARY KEY (batchId, documentId));Workflow with batches:
- Shanvi staff creates a
dispatch_batch(e.g., “10 passports, courier: Ram Bahadur, date: today”) - They assign document IDs to the batch
- All documents in the batch get their status updated atomically to
DISPATCHED_TO_SHANVIorVFS_AFTER_SHANVI - Agents can see which batch their application is in
Payment gate
Section titled “Payment gate”Before a document can move to stage 2 (DISPATCHED_TO_SHANVI) or stage 3 (DOCUMENT_AT_SHANVI), the agent must submit payment proof and Shanvi staff must verify it as PAID. Admin and HEAD_OFFICE bypass this gate.
Flow:
- Agent submits bulk payment proof (
POST /api/documents/bulk-payment) for multiple documents at once - Shanvi staff reviews and calls
PUT /api/documents/:id/verify-paymentwith{ paymentStatus: 'Paid' } - Only after verification can the document move forward in the pipeline
This is enforced in the backend at PUT /:id/status.
VFS rejection flow
Section titled “VFS rejection flow”If VFS rejects the visa application, the passport still needs to return to the client. The pipeline continues normally from stage 5 onward — only the outcome changes, not the physical process.
Stage 5 — VFS_COLLECTED status: REJECTED ↓ (passport still physically returns)Stage 6 — VFS_AFTER_SHANVI ↓Stage 7 — CONSULTANCY_RECEIVED ↓Stage 8 — CLOSEWhen a task is REJECTED, the original uploader can also change the assigned agent and resubmit (PUT /api/documents/:id/agent). This handles the case where the consultancy wants to reprocess through a different channel.
Multiple applicants per submission (participants)
Section titled “Multiple applicants per submission (participants)”A single task can cover multiple people traveling together (e.g., a family). The main documents record covers the primary applicant. Additional applicants are stored in document_participants:
CREATE TABLE document_participants ( id INTEGER PRIMARY KEY AUTOINCREMENT, documentId INTEGER NOT NULL, name TEXT, passportNumber TEXT, phoneNumber TEXT, documentFile TEXT, passportPhoto TEXT, applicationForm TEXT);Each participant’s documents are tracked under the parent task. The parent task’s status applies to all participants in the same submission.
Permission-gated status transitions
Section titled “Permission-gated status transitions”Each status transition requires the user’s agent type to have the corresponding permission:
| Target status | Required permission |
|---|---|
DOCUMENT_RECEIVER | DOCUMENT_RECEIVER |
DISPATCHED_TO_SHANVI | DOCUMENT_RECEIVER |
DOCUMENT_AT_SHANVI | DOCUMENT_AT_SHANVI |
VFS_RECEIVED | VFS_RECEIVED |
VFS_COLLECTED | VFS_AFTER_SHANVI |
VFS_AFTER_SHANVI | VFS_AFTER_SHANVI |
CONSULTANCY_RECEIVED | CONSULTANCY_RECEIVED |
CLOSE | TASK_CLOSE |
REJECTED | REJECT_TASK |
HEAD_OFFICE and ADMIN bypass all permission checks.
Known issue: These permission checks are currently only enforced on the frontend. The backend
PUT /:id/statusendpoint does not validate whether the caller’s agent type has the required permission before accepting the status change. Any authenticated agent can move any document to any status by calling the API directly. Fix: mirror thecheckCanManageTickets()pattern fromroutes/tickets.jsinto acheckCanUpdateVfs()function inroutes/documents.js.
Data visibility rules
Section titled “Data visibility rules”| Role | Sees |
|---|---|
| ADMIN | All documents |
| HEAD_OFFICE agent | All documents |
| DOCUMENT_RECEIVER / DOCUMENT_VERIFIER agent | All documents |
| Other AGENT | Only documents where agentId = userId OR uploadedBy = userId |
| USER (public) | Can track by passport number via /api/documents/track/:passportNumber — returns status only, no files |
API routes
Section titled “API routes”All routes under /api/documents. Authentication required except /track/:passportNumber.
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /track/:passportNumber | None | Public tracking by passport number |
GET | / | Token | List documents (scoped by role) |
GET | /my | Token | List only documents uploaded by the caller |
GET | /:id | Token | Single document detail |
GET | /:id/history | Token | Status change history for a document |
GET | /:id/participants | Token | Additional applicants for a document |
GET | /history/all | Token | All history entries for the caller’s documents |
POST | /upload | Token | Create task with documents (agent flow) |
POST | /admin/create | Token | Create task without documents (admin/draft flow) |
POST | /bulk-payment | Token | Submit payment proof for multiple documents |
PUT | /:id/status | Token | Update pipeline status |
PUT | /:id | Token | Edit document details (HEAD_OFFICE / ADMIN) |
PUT | /:id/agent | Token | Reassign agent when REJECTED |
PUT | /:id/verify-payment | Token | Mark payment as verified |
DELETE | /:id | Token | Soft-delete a document |
Frontend components
Section titled “Frontend components”| Component | Purpose |
|---|---|
VfsTrackingFlow.tsx | Visual pipeline — clickable stage cards with task counts per stage |
VfsList.tsx | Filterable table of all tasks; supports bulk actions |
VfsCreateTask.tsx | Form for agents to upload documents and create a task |
VfsTaskDetails.tsx | Single task view: status update controls, document viewer, history log |
VfsFilterBar.tsx | Filters: status, visa type, country, consultant, date range |
What needs to be built (priority order)
Section titled “What needs to be built (priority order)”- Server-side permission enforcement on status transitions — critical security gap (see Known Issues)
- Unique stage labels in UI — Stage 3 vs Stage 5, Stage 2 vs Stage 6 have identical display names
- Dispatch batch model —
dispatch_batches+batch_documentstables; bulk status update endpoint - Courier agent type — add Courier permission set; allow courier to update stages 2 and 6 on their batch
- Rejection flag on VFS_COLLECTED — store
vfsOutcome: 'APPROVED' | 'REJECTED'separately from pipeline status so status can continue flowing while the rejection is visible