Skip to content

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).


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 closed

Special 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 files
  • DRAFT — created via admin create form without documents attached
  • DELETED — 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.


StageStatusWho updatesNotes
1DOCUMENT_RECEIVERAgent / ConsultancyUploads applicant docs via portal
2DISPATCHED_TO_SHANVIShanvi staff (see Courier section)Confirms courier has picked up
3DOCUMENT_AT_SHANVIShanvi office staffVerifies docs, submits to VFS
4VFS_RECEIVEDShanvi office staffConfirms VFS receipt
5VFS_COLLECTEDShanvi office staffCollects from VFS (stamped or rejected)
6VFS_AFTER_SHANVIShanvi staff (see Courier section)Hands off to courier for return
7CONSULTANCY_RECEIVEDAgent / ConsultancyConfirms they received from courier
8CLOSEAgent / ConsultancyConfirms 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.

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 batch
CREATE 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:

  1. Shanvi staff creates a dispatch_batch (e.g., “10 passports, courier: Ram Bahadur, date: today”)
  2. They assign document IDs to the batch
  3. All documents in the batch get their status updated atomically to DISPATCHED_TO_SHANVI or VFS_AFTER_SHANVI
  4. Agents can see which batch their application is in

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:

  1. Agent submits bulk payment proof (POST /api/documents/bulk-payment) for multiple documents at once
  2. Shanvi staff reviews and calls PUT /api/documents/:id/verify-payment with { paymentStatus: 'Paid' }
  3. Only after verification can the document move forward in the pipeline

This is enforced in the backend at PUT /:id/status.


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 — CLOSE

When 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.


Each status transition requires the user’s agent type to have the corresponding permission:

Target statusRequired permission
DOCUMENT_RECEIVERDOCUMENT_RECEIVER
DISPATCHED_TO_SHANVIDOCUMENT_RECEIVER
DOCUMENT_AT_SHANVIDOCUMENT_AT_SHANVI
VFS_RECEIVEDVFS_RECEIVED
VFS_COLLECTEDVFS_AFTER_SHANVI
VFS_AFTER_SHANVIVFS_AFTER_SHANVI
CONSULTANCY_RECEIVEDCONSULTANCY_RECEIVED
CLOSETASK_CLOSE
REJECTEDREJECT_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/status endpoint 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 the checkCanManageTickets() pattern from routes/tickets.js into a checkCanUpdateVfs() function in routes/documents.js.


RoleSees
ADMINAll documents
HEAD_OFFICE agentAll documents
DOCUMENT_RECEIVER / DOCUMENT_VERIFIER agentAll documents
Other AGENTOnly documents where agentId = userId OR uploadedBy = userId
USER (public)Can track by passport number via /api/documents/track/:passportNumber — returns status only, no files

All routes under /api/documents. Authentication required except /track/:passportNumber.

MethodPathAuthDescription
GET/track/:passportNumberNonePublic tracking by passport number
GET/TokenList documents (scoped by role)
GET/myTokenList only documents uploaded by the caller
GET/:idTokenSingle document detail
GET/:id/historyTokenStatus change history for a document
GET/:id/participantsTokenAdditional applicants for a document
GET/history/allTokenAll history entries for the caller’s documents
POST/uploadTokenCreate task with documents (agent flow)
POST/admin/createTokenCreate task without documents (admin/draft flow)
POST/bulk-paymentTokenSubmit payment proof for multiple documents
PUT/:id/statusTokenUpdate pipeline status
PUT/:idTokenEdit document details (HEAD_OFFICE / ADMIN)
PUT/:id/agentTokenReassign agent when REJECTED
PUT/:id/verify-paymentTokenMark payment as verified
DELETE/:idTokenSoft-delete a document

ComponentPurpose
VfsTrackingFlow.tsxVisual pipeline — clickable stage cards with task counts per stage
VfsList.tsxFilterable table of all tasks; supports bulk actions
VfsCreateTask.tsxForm for agents to upload documents and create a task
VfsTaskDetails.tsxSingle task view: status update controls, document viewer, history log
VfsFilterBar.tsxFilters: status, visa type, country, consultant, date range

  1. Server-side permission enforcement on status transitions — critical security gap (see Known Issues)
  2. Unique stage labels in UI — Stage 3 vs Stage 5, Stage 2 vs Stage 6 have identical display names
  3. Dispatch batch modeldispatch_batches + batch_documents tables; bulk status update endpoint
  4. Courier agent type — add Courier permission set; allow courier to update stages 2 and 6 on their batch
  5. Rejection flag on VFS_COLLECTED — store vfsOutcome: 'APPROVED' | 'REJECTED' separately from pipeline status so status can continue flowing while the rejection is visible