DEVELOPER DOCUMENTATION / V1
Build with clarity.
A stateless assessment endpoint with a transparent scoring model.
01 / Quickstart
Create an account in the developer portal, create an API key and choose a paid API subscription. Keep the key on your server. Send a UUID Idempotency-Key header for each assessment.
curl -X POST "$VSA_BASE_URL/api/v1/vsa/assessments" \ -H "Authorization: Bearer $VSA_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 12345678-1234-4123-8123-123456789abc" \ --data @assessment.json
Base URL: https://checkmyvisa.co.uk. During private preview, API use is restricted to invited accounts, with a valid VSA key and paid subscription.
Test your key without a charge
Sign in, choose an agency account, then open API keys. Create and save a key, paste it into Test your API key, and select Test connection. No subscription is needed for this connection test.
From a terminal, set VSA_API_KEY securely and run:
curl -X POST "https://checkmyvisa.co.uk/api/v1/vsa/key-check" -H "Authorization: Bearer $VSA_API_KEY"
A 200 response with valid: true confirms the key and linked account can authenticate. assessment_units_used is 0. It does not verify payment, document extraction or scoring. A full assessment uses the endpoint above and requires a paid subscription. Missing or invalid keys return 401; accounts outside the preview return 403; unavailable services return 503. A legacy Unkey credit limit can return 402.
API access with Unkey
Open API keys after signing in. Create a VSA key and copy it immediately: the full key is shown once. Use it only on your server as an Authorization: Bearer header. You do not need your own Unkey account to call VSA.
Unkey verifies whether a key is valid, enabled and within configured limits. VSA additionally checks the issuing API, linked user, private-preview access and paid monthly allowance. Keys for one user share the subscription allowance; creating another key does not reset it.
Starter: £425/month for 200 completed API checks. Business: £1,275/month for 1,000. Requests stop at the allowance; extra usage is not automatically charged. Manage your plan from the workspace.
Retrying and rotating keys
Use a new UUID Idempotency-Key for each new assessment. Keep the same key when retrying the same request after a network interruption. A duplicate returns 409; results are not replayed. Save successful responses. To rotate a key, create a replacement, update your server secret, then ask the administrator to revoke the old key in Unkey.
Administrators: keep UNKEY_ROOT_KEY server-only and scope it to reading, creating and verifying keys in the VSA keyspace. The application links the confirmed email through externalId and checks the Supabase user ID in server-controlled key metadata.
Sources: Unkey quickstart · Unkey identities. Reviewed 16 September 2026.
Student document readiness
Use a Student envelope: {"route":"student","facts":{"student":{...}},"documents":[...]}. The downloadable fictional questionnaire shows facts.student fields. Documents use the pack types including cas, atas_certificate and tb_certificate. is_verified records your review, not issuer authentication.
The response includes kind: student, score (null when unresolved), score_range, status, checks with official sources, required_funds_gbp and the ruleset version. This is a checklist-completion measure, not a probability or UKVI points. A failed check always requires action. Complex funding, dependants and in-country cases require review. Legacy Student payloads are rejected with STUDENT_SCHEMA_REQUIRED; single-document extraction is not a Student assessment.
02 / Request schema
Send all required fields as JSON. Dates use YYYY-MM-DD, amounts are GBP, and verification flags are explicit assertions from your own document checks. Unknown values must be resolved before assessment.
{
"applicant": {
"passport_expiry_date": "2030-12-31",
"nationality": "GBR"
},
"trip": {
"start_date": "2027-04-01",
"end_date": "2027-04-14",
"estimated_cost_gbp": 2000,
"visa_type": "tourist"
},
"finances": {
"available_funds_gbp": 6500,
"monthly_net_income_gbp": 3000,
"has_unexplained_large_deposits": false
},
"ties": {
"employment_status": "employed",
"owns_property": false,
"has_dependents_in_home_country": false
},
"history": {
"previous_visa_refusals": 0,
"travel_history_count": 4
},
"documents": [
{
"type": "passport",
"is_verified": true
},
{
"type": "bank_statement",
"is_verified": true
},
{
"type": "employment_letter",
"is_verified": true
},
{
"type": "accommodation",
"is_verified": true
}
]
}Optional extension: trip.purpose_justification is a string documenting the reason for a tourist stay longer than 90 days. Without it, the duration deduction applies.
Supported document types: passport, bank_statement, employment_letter, accommodation and itinerary. Occupation evidence, including student or self-employment evidence, uses the employment_letter slot in this version.
03 / Response
Score and evidence coverage are independent. Identical validated inputs produce identical scores and findings; the timestamp records when the response was generated.
{
"score": 90,
"outcome_band": "Strong",
"evidence_coverage_level": "High",
"coverage_percentage": 100,
"deductions_by_category": {
"eligibility": 0,
"financial_affordability": 0,
"document_consistency": 0,
"reasons_to_return": 10,
"travel_history": 0,
"duration_vs_purpose": 0
},
"findings": [
{
"category": "reasons_to_return",
"severity": "warning",
"deduction": 10,
"message": "No property or dependents declared in the home country.",
"finding_id": "711b5356-1f1d-482a-af4c-070012dd7cc6",
"document_id": "application",
"document_type": "questionnaire",
"rule_pack_id": "ukvi.visitor.v1",
"code": "RETURN_TIES_WEAK",
"tier": "RPF",
"field_path": "reasons_to_return",
"evidence": {
"extracted": "No property or dependents declared in the home country.",
"expected": "Category cap 20"
},
"cross_ref": [],
"score_delta": -10,
"queue_human": false,
"category_key": "reasons_to_return",
"lifecycle_state": "OPEN",
"dedupe_key": "ukvi.visitor.v1|application|RETURN_TIES_WEAK|reasons_to_return",
"adverse": true
}
],
"timestamp": "2026-09-13T12:00:00.000Z",
"rule_pack_status": "QA",
"scanning_live": false
}04 / Scoring rules
Start at 100 and subtract deductions, capped separately in each category. The final score cannot fall below 0. These are the configured VSA readiness rules; they do not represent destination-specific legal requirements or approval probabilities.
| Category | Rules | Cap |
|---|---|---|
| Eligibility | Passport expires before six calendar months after the trip ends: −20. Missing or unverified passport: −20. | 20 |
| Financial affordability | Funds below trip cost: −25; otherwise funds below 1.5 × cost: −15. Unexplained large deposits: −10. Net income ≤ 0 for non-students: −10. | 25 |
| Document consistency | End date on or before start date: −20. Flagged bank statement or itinerary discrepancies: −10 per type. | 20 |
| Reasons to return | Unemployed: −15. No property and no dependents at home: −10. | 20 |
| Travel history | Any previous refusal: −10. No previous travel: −5. | 10 |
| Duration vs purpose | Tourist stay over 90 days without purpose_justification: −5. | 5 |
Strong: 85–100. Moderate: 65–84. High Risk: 0–64. Findings remain visible after a category cap is reached; their deduction reports the amount actually applied, which may be 0.
For consistency flags, use issues: ["date_discrepancy"]. Issue strings containing discrepancy, mismatch, inconsisten or conflict trigger the rule. A document type is deducted at most once. Tourist aliases include tourist, tourist visa, standard tourist and e-tourist.
05 / Evidence coverage
Four evidence slots each contribute 25%: passport; bank statement; occupation; and accommodation or itinerary. Only documents with is_verified set to true count. Duplicate documents never increase coverage. Providing both accommodation and itinerary still fills one slot.
| Level | Coverage | Verified slots |
|---|---|---|
| High | ≥90% | 4 of 4 |
| Medium | 75–89% | 3 of 4 |
| Low | 50–74% | 2 of 4 |
| Insufficient | <50% | 0–1 of 4 |
06 / Optional document extraction
Instead of the direct request, submit a document envelope with exactly one document_url or document_base64. Supply known application facts in assessment. Maximum base64 file size on Netlify: 3 MiB (4 MiB for approved URL imports). Supported types: PDF, JPEG and PNG. Base64 must be raw, without a data URL prefix.
{
"document_base64": "BASE64_FILE_BYTES",
"mime_type": "application/pdf",
"assessment": {
"trip": {
"start_date": "2027-04-01",
"end_date": "2027-04-14",
"estimated_cost_gbp": 2000,
"visa_type": "tourist"
},
"finances": {
"available_funds_gbp": 6500,
"monthly_net_income_gbp": 3000,
"has_unexplained_large_deposits": false
},
"ties": {
"employment_status": "employed",
"owns_property": false,
"has_dependents_in_home_country": false
},
"history": {
"previous_visa_refusals": 0,
"travel_history_count": 4
},
"documents": []
}
}URLs must use HTTPS on an administrator-approved hostname and return the file directly. Redirects are rejected. OpenAI through Netlify AI Gateway receives the document and context for extraction; VSA does not store assessment payloads or documents in its database. Extraction requests use store:false; provider processing follows Netlify and OpenAI terms.
Extraction never determines the score. Incomplete or unreadable results are rejected. OCR cannot verify evidence; only verification assertions explicitly supplied in assessment.documents can be retained.
07 / Errors & usage
| HTTP | Meaning |
|---|---|
| 200 | Assessment completed |
| 400 | Missing body or invalid JSON |
| 401 | Missing, invalid, expired, disabled or wrong-API key |
| 403 | Account is not invited to the private preview |
| 402 | Paid subscription required or monthly allowance exceeded |
| 409 | Request already in progress, completed, or reused with different data |
| 413 | Request over 4.3 MB or imported document over 4 MiB |
| 415 | Content-Type must be application/json |
| 422 | Invalid fields, disallowed document source or incomplete extraction |
| 429 | Rate limit exceeded |
| 502 | Document extraction provider unavailable |
| 503 | Integration not configured or key provider unavailable |
Each completed assessment uses one unit from the account’s monthly subscription allowance. Invalid requests and failed extractions do not use a unit. Allowances reset on the paid Stripe billing period, not the calendar month. A UUID Idempotency-Key identifies each request; concurrent duplicates and completed duplicates return 409 without another charge. Results are not stored or replayed: keep successful responses in your own system. Expired reservations are released after two minutes. Overage charging is disabled until a per-check rate and customer opt-in are configured.
Every assessment response uses Cache-Control: private, no-store. Never put API keys in URLs, browser bundles or public logs.

