travel.book_visa_assistance — Full Intent Specification
INTENT NAMESPACE: travel
INTENT NAME: book_visa_assistance
FULL ID: travel.book_visa_assistance
VERSION: v1.0.0
STATUS: draft
TTBS WEIGHTS: time 0.30 · taste 0.15 · budget 0.20 · safety 0.35
LAST UPDATED: 2026-05-14
User engages a visa-application assistance service — document checklist, form completion, biometric appointment, courier-to-embassy, status tracking. Distinct from direct embassy / consulate booking (which is the underlying government action) — TOMO's partner orchestrates the assistance, not the visa issuance. Distinct from travel.book_package (which may include visa as a sub-component). Partner exemplars: VFS Global, BLS International, TLScontact, OneVasco, Atlys, Stamp This Passport, Thomas Cook Visa, MakeMyTrip Visa.
1. NATURAL LANGUAGE COVERAGE
Classifies IN
- "visa for Thailand"
- "Schengen visa from Hyderabad"
- "USA tourist visa appointment"
- "UK visa application help"
- "Dubai visa on arrival or e-visa"
- "Japan visa documents"
- "Singapore visa for next month"
- "visa renewal Canada"
- "fast track visa Australia"
- "visa for kids travel UK"
Classifies OUT — borderline NO
- "buy flight to Bangkok" →
travel.book_flight - "hotel Bali" →
travel.book_hotel - "passport renewal" → out of scope v1 (route to government Passport Seva Kendra)
- "OCI card" — out of scope v1
- "PR / work visa lawyer" — out of scope v1
MULTI-INTENT TRIGGERS
- "Schengen visa + travel insurance" →
travel.book_visa_assistance+finance.buy_travel_insurance - "Visa + flight + hotel" →
travel.book_visa_assistance+travel.book_flight+travel.book_hotel - "Visa + forex" →
travel.book_visa_assistance+travel.book_forex
2. INPUT — TOMO → PROVIDER
{
"intent": "travel.book_visa_assistance",
"intent_version": "v1.0.0",
"request_id": "req_vis_2h4k_2026-05-14T10:00:00+05:30",
"user_session_id": "anon_user_token_or_uid",
"visa_request": {
"destination_country_code": "TH",
"visa_kind": "tourist",
"visa_sub_kind": "single_entry",
"travel_start_date": "2026-08-10",
"travel_end_date": "2026-08-20",
"duration_of_stay_days": 10,
"applicant_count": 2,
"applicants_profile_summary": [
{
"applicant_kind": "adult",
"passport_country_code": "IN",
"passport_validity_months_remaining": 14,
"prior_travel_history_summary": "ASEAN_visited_within_3yr",
"salaried_or_business": "salaried",
"estimated_income_band_inr": "10_to_25_lakh",
"previous_visa_refusals": false,
"minor_under_18": false
},
{
"applicant_kind": "adult",
"passport_country_code": "IN",
"passport_validity_months_remaining": 14,
"prior_travel_history_summary": "ASEAN_visited_within_3yr",
"salaried_or_business": "salaried",
"estimated_income_band_inr": "10_to_25_lakh",
"previous_visa_refusals": false,
"minor_under_18": false
}
],
"application_mode_preferred": "e_visa",
"biometric_required": false,
"submission_city": "Hyderabad",
"premium_service_preferred": false,
"express_processing_required": false,
"budget_band": "good",
"budget_max_inr_total": 8000
},
"context": {
"user_locale": "en-IN",
"user_currency_pref": "INR",
"trust_signals": {
"is_repeat_customer": false,
"user_account_age_days": 312
}
}
}
| Field | Type | Constraint | Notes |
|---|---|---|---|
intent |
string | REQUIRED, equals "travel.book_visa_assistance" |
|
visa_request.destination_country_code |
string | REQUIRED, ISO_3166_2 | Validated against partner coverage |
visa_request.visa_kind |
enum | REQUIRED, STRICT §6 | tourist / business / transit / medical / student / employment / dependant / journalist |
visa_request.visa_sub_kind |
enum | REQUIRED, STRICT §6 | single_entry / multiple_entry / on_arrival / e_visa / sticker / long_stay |
visa_request.travel_start_date |
ISO_DATE | REQUIRED | Drives processing-timeline filter |
visa_request.duration_of_stay_days |
int | REQUIRED, 1-365 | |
visa_request.applicant_count |
int | REQUIRED, 1-20 | |
visa_request.applicants_profile_summary |
array | REQUIRED, length = applicant_count | Per-applicant summary |
visa_request.applicants_profile_summary[].passport_country_code |
string | REQUIRED, ISO_3166_2 | Passport-issuing country |
visa_request.applicants_profile_summary[].passport_validity_months_remaining |
int | REQUIRED, ≥0 | Most countries require ≥6 mo |
visa_request.applicants_profile_summary[].previous_visa_refusals |
bool | REQUIRED | Triggers extra-care advisory |
visa_request.application_mode_preferred |
enum | REQUIRED, STRICT §6 | e_visa / sticker_at_consulate / sticker_at_vfs / on_arrival |
visa_request.biometric_required |
bool | REQUIRED | Drives in-person VFS visit |
visa_request.express_processing_required |
bool | REQUIRED | Premium fast-track |
Anti-fabrication preamble: no paid placement. TOMO never represents that a visa will be approved. Partner provides assistance only — issuance is the embassy / consulate's sovereign decision. Partner cannot guarantee outcome. Partners offering "guaranteed visa" = ingest reject + suspension.
3. PROVIDER TOOLS
Tool 1: search_visa_services
PURPOSE: return assistance offerings for country + visa kind + applicants
INPUT: §2 request body
OUTPUT: { services: VisaServiceOption[], result_token, expires_at }
SLA: p50 < 800ms, p95 < 2500ms
RATE LIMIT: ≤ 1/sec per (user_session_id, partner)
RESULT SET: up to 10 services
Tool 2: get_visa_service_detail
PURPOSE: full document checklist + sample-rejection reasons + timeline
INPUT: { service_id, request_id }
OUTPUT: VisaServiceDetail (§5)
SLA: p95 < 1500ms
Tool 3: hold_appointment_slot
PURPOSE: soft-lock VFS / BLS / consulate biometric slot for 30 min
INPUT: { service_id, slot_id, applicants, request_id }
OUTPUT: { hold_id, hold_expires_at, locked_price_inr }
SLA: p95 < 1500ms
Tool 4: confirm_visa_engagement
PURPOSE: confirm engagement + document upload portal + payment lock
INPUT: { hold_id, payment_method_id, request_id }
OUTPUT: ConfirmedVisaEngagement (§5)
SLA: p95 < 3500ms
IDEMPOTENCY: request_id
Tool 5: submit_documents
PURPOSE: encrypted document upload (passport scan, ITRs, bank statements, photos)
INPUT: { engagement_id, document_kind, file_payload, request_id }
OUTPUT: { document_id, accepted, rejection_reason_code }
SLA: p95 < 5000ms
RATE LIMIT: 50 uploads / engagement
Tool 6: check_application_status
PURPOSE: poll visa application status
INPUT: { engagement_id, request_id }
OUTPUT: ApplicationStatus (§5)
SLA: p95 < 1500ms
Tool 7: cancel_engagement
PURPOSE: cancel + tiered refund per stage policy
INPUT: { engagement_id, reason_code, request_id }
OUTPUT: CancellationResult (§5)
SLA: p95 < 2500ms
4. RESPONSE SHAPE
VisaServiceOption
VisaServiceOption:
service_id: { type: string, constraint: REQUIRED, opaque }
provider:
provider_id: { type: string, constraint: REQUIRED }
name: { type: string, constraint: REQUIRED, semantics: "e.g. 'VFS Global', 'BLS International'" }
authorized_for_country: { type: boolean, constraint: REQUIRED, semantics: "TRUE = appointed by destination embassy/consulate for IN applicants" }
embassy_authorization_proof_url: { type: string, constraint: REQUIRED, HTTPS URL }
years_in_market: { type: int, constraint: REQUIRED, ≥0 }
iata_or_dot_registration: { type: string, constraint: REQUIRED, semantics: "Empty when not applicable; visa-only agencies may not need IATA" }
destination_country_code: { type: string, constraint: REQUIRED, ISO_3166_2 }
visa_kind: { type: enum, constraint: REQUIRED, STRICT §6 }
visa_sub_kind: { type: enum, constraint: REQUIRED, STRICT §6 }
application_mode: { type: enum, constraint: REQUIRED, STRICT §6 }
processing_timeline:
typical_business_days_min: { type: int, constraint: REQUIRED, ≥0 }
typical_business_days_max: { type: int, constraint: REQUIRED, ≥0 }
express_business_days_min: { type: int, constraint: REQUIRED, ≥0 }
express_business_days_max: { type: int, constraint: REQUIRED, ≥0 }
last_safe_submission_date: { type: string, constraint: REQUIRED, ISO_DATE }
pricing:
government_visa_fee_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0, semantics: "Pass-through to embassy/consulate" }
service_fee_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0, semantics: "VFS / BLS service fee" }
courier_fee_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
biometric_fee_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
express_premium_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
gst_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
total_per_applicant_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
total_for_party_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
submission_centers:
type: array<object>
constraint: REQUIRED, ≥1
shape:
city: { type: string, constraint: REQUIRED }
address: { type: string, constraint: REQUIRED }
next_available_slot: { type: string, constraint: REQUIRED, ISO_DATETIME }
documents_checklist: { type: array<enum>, constraint: REQUIRED, STRICT §6, ≥1 }
applicant_eligibility_notes: { type: array<string>, constraint: REQUIRED, may be empty }
approval_rate_pct:
type: float
constraint: REQUIRED, 0-100
semantics: "Embassy's published or platform-tracked approval rate for IN applicants in past 12 months"
rating:
average_score: { type: float, constraint: REQUIRED, 0.0-5.0 }
review_count: { type: int, constraint: REQUIRED, ≥0 }
partner_reference:
source: { type: string, constraint: REQUIRED }
deeplink: { type: string, constraint: REQUIRED, HTTPS URL }
VisaServiceDetail
VisaServiceDetail:
service_id: { type: string, constraint: REQUIRED }
documents_full_list_with_explanations:
type: array<object>
constraint: REQUIRED, ≥1
shape:
document_kind: { type: enum, constraint: REQUIRED, STRICT §6 }
required: { type: boolean, constraint: REQUIRED }
format_spec: { type: string, constraint: REQUIRED, semantics: "PDF/PNG/JPG, dimensions, color etc." }
explanation: { type: string, constraint: REQUIRED }
sample_url: { type: string, constraint: REQUIRED, HTTPS URL, semantics: "Empty when no sample" }
common_rejection_reasons: { type: array<string>, constraint: REQUIRED, ≥3 }
passport_validity_required_months: { type: int, constraint: REQUIRED }
photo_specifications: { type: string, constraint: REQUIRED }
financial_proof_floor_inr_per_day: { type: int, constraint: REQUIRED, ≥0 }
travel_insurance_required: { type: boolean, constraint: REQUIRED }
travel_insurance_minimum_cover_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0, semantics: "0 when not required" }
ConfirmedVisaEngagement
ConfirmedVisaEngagement:
engagement_id: { type: string, constraint: REQUIRED, immutable }
service_id: { type: string, constraint: REQUIRED }
applicants: { type: array<object>, constraint: REQUIRED, ≥1, shape: { name, passport_number_masked } }
appointment:
scheduled_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
center_address: { type: string, constraint: REQUIRED }
biometric_required: { type: boolean, constraint: REQUIRED }
documents_upload_portal_url: { type: string, constraint: REQUIRED, HTTPS URL }
documents_due_by: { type: string, constraint: REQUIRED, ISO_DATETIME }
total_paid_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
invoice_url: { type: string, constraint: REQUIRED, HTTPS URL }
expected_decision_window:
earliest: { type: string, constraint: REQUIRED, ISO_DATE }
latest: { type: string, constraint: REQUIRED, ISO_DATE }
passport_return_courier_url: { type: string, constraint: REQUIRED, HTTPS URL, semantics: "Empty until passport in transit" }
emergency_support:
helpline_phone: { type: string, constraint: REQUIRED, E.164 }
helpline_email: { type: string, constraint: REQUIRED }
ApplicationStatus
ApplicationStatus:
engagement_id: { type: string, constraint: REQUIRED }
status:
type: enum
constraint: REQUIRED, STRICT §6
values: [docs_pending, docs_in_review, appointment_scheduled, biometric_done, submitted_to_embassy, in_processing, additional_docs_requested, decision_made, passport_in_transit, completed_approved, completed_rejected, withdrawn]
reason_code: { type: enum, constraint: REQUIRED, STRICT §6, may be 'none' }
additional_docs_required: { type: array<string>, constraint: REQUIRED, may be empty }
last_updated_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
estimated_resolution_date: { type: string, constraint: REQUIRED, ISO_DATE }
CancellationResult
CancellationResult:
engagement_id: { type: string, constraint: REQUIRED }
cancelled_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
stage_at_cancellation: { type: enum, constraint: REQUIRED, STRICT §6 }
refund_amount_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
refund_method: { type: enum, constraint: REQUIRED, STRICT §6, values: [original_payment, bank_transfer] }
refund_eta_days: { type: int, constraint: REQUIRED, 0-30 }
non_refundable_government_fee_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0, semantics: "Government visa fee is non-refundable once submitted" }
FORBIDDEN FIELDS
paid_placement_score,ad_bid,sponsored_rank,featured_visa_serviceeditor_pick,tomo_recommended,top_choiceapproval_guarantee_text,guaranteed_visa_in_X_days_textembassy_authorization_proof_fakeinflated_approval_rate_pctcommission_padded_service_feeconfidential_embassy_communication_disclosed(any embassy direct comms are confidential)applicant_real_namesin reviews
5. CONTROLLED VOCABULARIES
visa_kind:
values: [tourist, business, transit, medical, student, employment, dependant, journalist, official, diplomatic]
visa_sub_kind:
values:
single_entry, multiple_entry, on_arrival, e_visa, sticker, long_stay,
short_stay_schengen_C, schengen_long_D, transit_airport_A, transit_C
application_mode_preferred / application_mode:
values: [e_visa, sticker_at_consulate, sticker_at_vfs, sticker_at_bls, on_arrival, courier_only]
applicants_profile_summary[].passport_country_code:
values: ISO_3166_2 list (full)
applicants_profile_summary[].salaried_or_business:
values: [salaried, self_employed, business, student, dependant, retired, unemployed_disclosed]
applicants_profile_summary[].estimated_income_band_inr:
values: under_5_lakh, 5_to_10_lakh, 10_to_25_lakh, 25_to_50_lakh, 50_to_1cr, above_1cr
applicants_profile_summary[].prior_travel_history_summary:
values:
none, ASEAN_visited_within_3yr, Schengen_visited_within_3yr,
US_UK_visited_within_3yr, Australia_NZ_visited_within_3yr,
Visited_5_plus_countries_within_5yr, no_recent_international
documents_checklist / documents_full_list_with_explanations.document_kind:
values:
passport_first_last_page, passport_all_used_pages, photograph_specs_compliant,
cover_letter, application_form_filled, bank_statement_6_month,
itr_3_years, salary_slip_3_month, employment_letter, business_proof,
student_id, school_admission_letter, hotel_booking_proof,
flight_booking_proof, travel_insurance_proof, sponsor_letter_NOC,
sponsor_financial_proof, marriage_certificate, birth_certificate_minor,
parental_consent_minor, gst_registration_business, partnership_deed,
pan_card, aadhaar, no_objection_certificate
ApplicationStatus.reason_code:
values:
none, docs_incomplete, photo_non_compliant, financial_proof_insufficient,
travel_history_concern, prior_refusal_concern, sponsor_doc_concern,
interview_required, additional_verification, embassy_internal,
rejected_security, rejected_misrepresentation, rejected_insufficient_ties,
rejected_other
CancellationResult.stage_at_cancellation:
values:
pre_documents, documents_partial, documents_complete,
appointment_scheduled, submitted_to_embassy, decision_made_pre_return
CancellationResult.refund_method:
values: [original_payment, bank_transfer]
6. TTBS DIMENSIONS
TIME (weight = 0.30):
signals_used:
- processing_timeline.typical_business_days_max vs days-until-travel
- last_safe_submission_date proximity
- submission_centers[].next_available_slot
- express_business_days_min when express_processing_required=true
weighting:
timeline_fit: 0.45
last_safe_buffer: 0.25
next_slot: 0.20
express_speed: 0.10
user_band_handling:
fast: prefer express_premium-priced services
balanced: standard
flexible: choose slower cheaper provider when timeline allows
TASTE (weight = 0.15):
signals_used:
- submission_city matches user_location
- application_mode_preferred match
- DNA repeat provider
weighting:
city_match: 0.50
mode_match: 0.30
dna: 0.20
BUDGET (weight = 0.20):
signals_used:
- pricing.total_per_applicant_inr
- pricing.total_for_party_inr
- express_premium_inr (when applicable)
weighting:
per_applicant: 0.45
total: 0.40
express_premium: 0.15
SAFETY (weight = 0.35):
signals_used:
- provider.authorized_for_country (HARD filter when destination's embassy
has appointed an exclusive partner — partner must be that one or
ingest reject)
- provider.embassy_authorization_proof_url verifiable
- approval_rate_pct (signal of expert handling; not embassy outcome)
- rating.average_score ≥4.0 floor
- documents_full_list_with_explanations completeness
- financial_proof_floor_inr_per_day disclosed clearly
- travel_insurance_required + minimum_cover stated
weighting:
authorized: 0.40
embassy_proof: 0.15
approval_rate: 0.10
rating_floor: 0.15
docs_completeness: 0.10
proof_disclosures: 0.10
user_band_handling:
fast: relax rating to 3.8 (when authorized=true)
balanced: floor 4.0
great: only embassy-authorized provider regardless of price
Locked weights: time 0.30 / taste 0.15 / budget 0.20 / safety 0.35. Time is critical (travel date is a hard deadline). Safety enforces embassy-authorization hard filter where applicable (e.g., VFS holds exclusive UK/Schengen IN-applicant rights).
7. COMPLETION CONTRACT
POST /api/v1/cpc/mcp_provider/<your_partner_id>
Body:
{
"intent": "travel.book_visa_assistance",
"external_id": "<engagement_id>",
"request_id": "<request_id>",
"amount_inr": 400, // NET partner commission only
"gst_inr": 72,
"tips_inr": 0,
"pass_through_inr": 7200, // government visa fee + service fee + courier + biometric
"closed_at": "2026-07-25T17:00:00+05:30",
"status": "completed",
"destination_country_code": "TH",
"visa_kind": "tourist",
"visa_sub_kind": "e_visa",
"applicant_count": 2,
"approval_outcome": "approved" // approved / rejected / withdrawn
}
User pays full ticket. Partner's commission (~₹400) is amount_inr. Government fee + VFS / BLS service fees + courier + biometric ride in pass_through_inr. TOMO charges 10% × ₹400. HMAC-SHA256.
Rejected visa applications still bill the service fee — government fee non-refundable. Status completed regardless of approval outcome; outcome is tracked separately.
8. WIDGET
VisaServiceListingWidget.
Field mapping:
- provider.name + "Embassy-authorized" badge when authorized_for_country=true
- destination_country_code + visa_kind chip
- application_mode chip
- processing_timeline.typical_business_days_max → "5-7 working days"
- last_safe_submission_date → "Submit by X to make travel"
- pricing.total_for_party_inr → big price
- approval_rate_pct → "92% approval (IN, past 12mo)" pill
- rating.average_score + review_count → star pill
- submission_centers.next_available_slot → "Earliest slot: Tue 10 AM"
9. CACHING POLICY
| Call | TTL | Rationale |
|---|---|---|
| search_visa_services | 5min | Slot volatility moderate |
| get_visa_service_detail | 24h | Checklist stable |
| hold_appointment_slot | NO CACHE | Stateful lock |
| confirm_visa_engagement | NO CACHE | Idempotent |
| submit_documents | NO CACHE | Content-addressed |
| check_application_status | 30min | Status changes slowly |
| cancel_engagement | NO CACHE | — |
| Embassy authorization verification | 30d | Updates rare |
10. ERROR CODES
| Code | HTTP | Meaning | Retry |
|---|---|---|---|
INVALID_REQUEST |
400 | Malformed | No |
RATE_LIMITED |
429 | Throttle | 1, 2s |
INTERNAL_ERROR |
500 | Partner failure | 2, exp |
SIGNATURE_INVALID |
401 | HMAC fail | No |
NO_SERVICE_MATCH |
200 (empty) | No service for country+kind | n/a |
SLOT_TAKEN |
409 | Hold race-lost | UI re-queries |
HOLD_EXPIRED |
410 | Hold elapsed | UI re-holds |
NOT_AUTHORIZED_PROVIDER |
422 | Provider not embassy-appointed for IN | No |
PASSPORT_VALIDITY_INSUFFICIENT |
422 | <6mo validity at return date | No |
TIMELINE_INFEASIBLE |
422 | Even express won't make travel date | No |
DOCUMENT_FORMAT_REJECTED |
422 | Photo / scan non-compliant | No (UI re-uploads) |
PAYMENT_FAILED |
402 | Gateway declined | No |
EMBASSY_REJECTED |
200 (advisory) | Final embassy decision | n/a |
ADDITIONAL_DOCS_REQUESTED |
200 (advisory) | Embassy follow-up | n/a |
CANCELLED_PAST_GOV_SUBMIT |
410 | Government fee non-refundable | No |
BIOMETRIC_NO_SHOW |
422 | Applicant missed biometric | No (re-book) |
11. SANDBOX → PRODUCTION CHECKLIST
[ ] All seven tools implemented
[ ] At least 30 destination countries supported
[ ] Embassy-authorized provider mapping verified per country
[ ] embassy_authorization_proof_url verifiable (signed document)
[ ] approval_rate_pct from past 12 months IN-applicant data (not global)
[ ] Document format specs match current embassy requirements
[ ] Common rejection reasons curated from real embassy feedback
[ ] last_safe_submission_date computation correctly accounts for embassy
+ courier + buffer
[ ] HMAC signing verified on test CPC webhook
[ ] amount_inr in CPC is COMMISSION (NET); pass_through_inr = gov fee + VFS + courier + biometric
[ ] All controlled vocabularies respected
[ ] No forbidden fields anywhere
[ ] SLA p95 met
[ ] Document upload endpoint encrypts at rest; auto-deletes 90 days post-decision
[ ] No client real names in reviews
[ ] Privacy policy + grievance officer contact uploaded
[ ] Customer support: 24×7 visa helpline + emergency escalation
[ ] Rejection-handling SOP reviewed (no false-hope language)
12. ANTI-FABRICATION RULES
RULE 1: provider.authorized_for_country must be verifiable for the
destination country. Many embassies (UK, Schengen-area) have
exclusive partners (e.g., VFS for UK from IN); other providers
cannot serve those routes. Falsely claiming authorization =
customer-harm + suspension.
RULE 2: No paid_placement, ad_bid, sponsored_rank, featured_visa_service.
RULE 3: NO approval guarantees. "Guaranteed visa in 7 days" / "100%
approval" / "money-back guarantee" forbidden. Visa issuance is
sovereign embassy decision; partner cannot guarantee.
TOMO ingest scans for these phrases.
RULE 4: approval_rate_pct must be from past 12 months of IN applicants
through this provider for this visa_kind. Partner inflating with
global / cross-product / older data = ingest reject.
RULE 5: rating.average_score from completed engagements only;
review_count from completed engagements only.
RULE 6: No client real names in reviews — applicant identifiability is
sensitive PII for refused-visa applicants.
RULE 7: Embassy-direct communications (interview transcripts, internal
reason codes from embassy) must not be disclosed. Partner
publishing = privacy breach + ingest reject.
RULE 8: amount_inr is partner COMMISSION only. Government fee + VFS / BLS
service fee + courier + biometric are pass_through_inr. Inflated
commission base = audit.
RULE 9: Document upload endpoint must encrypt at rest; auto-delete 90
days post-decision. Visa documents (bank statements, ITRs) are
sensitive PII; perpetual retention forbidden.
RULE 10: Rejection-handling must use plain factual language ("Embassy
did not approve"); partner cannot blame applicant or speculate
on reasons unless embassy explicitly disclosed them.
RULE 11: Information-completeness score (hidden rank factor weight 0.10)
rewards full §4 shape.
RULE 12: last_safe_submission_date must reflect realistic worst-case
processing + courier + travel-buffer. Aggressive estimates that
cause missed travel = customer-harm.
RULE 13: Government visa fee correctly identified as non-refundable
once submitted; partner cannot hide this from refund table.
RULE 14: photo_specifications must match current embassy spec sheet;
outdated specs = guaranteed rejection = customer-harm.
RULE 15: For minors, parental_consent_minor + birth_certificate_minor
must be in checklist; skipping = embassy rejection guaranteed.
VERSION HISTORY
v1.0.0 — 2026-05-14 — Initial spec. Embassy-authorized provider mapping
per country (VFS / BLS / TLScontact / direct).
No approval guarantees (regulatory).
Approval-rate from past 12 months IN-applicants only.
Net-commission base. Document encryption + 90-day
auto-delete. Government fee non-refundable
once submitted.