Version 3.3 · Last updated 2026-09-24
This guide covers the REST API your system uses to create a candidate, submit their verification details, track progress, and download the final report. A Postman collection is available alongside this guide.
Overview — the core flow
Every integration follows five calls:
GET /external/v1/candidate/packages — Read your company's configuration: packages, required fields, categories, tags, custom fields, and add-on catalogue.
POST /external/v1/candidate/add — Create a candidate and receive a candidate_id.
POST /external/v2/candidate/submit-bgv — Submit the candidate's check data.
GET /external/v1/candidate/details — Track status and read per-check results.
GET /external/v1/candidate/report/pdf — Download the completed report.
A few things apply to every endpoint:
Authentication
SpringVerify provisions your company with an API token. Send it on every request:
Authorization: Bearer <YOUR_API_TOKEN>Content-Type: application/json
Keep the token secret; it is a long-lived credential. A missing or invalid token returns 401 Unauthorized.
Base URL and environments
https://api-acceptance-2-sa.in.springverify.comhttps://api-sa.in.springverify.comYour API token belongs to one environment. Package ids, category ids, tag ids and custom fields also differ between sandbox and production — read GET /packages in each environment rather than reusing values.
1. GET /external/v1/candidate/packages
Returns your company's configuration. Call this first and cache the result. There are no request fields; the configuration is derived from the token.
Sample request:
curl --location 'https://api-acceptance-2-sa.in.springverify.com/external/v1/candidate/packages' \ --header 'Authorization: Bearer <YOUR_API_TOKEN>'
Key response fields:
required_fields — Candidate fields that POST /add rejects if missing (e.g. EMAIL, PHONE, EMPLOYEE_ID).packages[].subtype_id — The value you pass to POST /add. It fully identifies the package.packages[].config — What the package runs (identity, address, employment, education, court, etc.) and the counts validated at submit.addons — Optional checks available per candidate with your negotiated price.categories and tags — Company-defined labels for organising candidates.credits — Your current credit balance.2. POST /external/v1/candidate/add
Creates a candidate against a package and returns a candidate_id. The candidate lands in Awaiting Input.
Key request fields:
candidate.name (required) — Letters, spaces, dot, and hyphen only. Digits are rejected.candidate.email / candidate.phone — Required if they appear in your required_fields.candidate.invite (required) — false: you submit all data by API. true: the candidate fills the form (requires email).package.subtype_id (required) — From GET /packages.candidate.resume — Résumé as an https URL or a base64 object { content, content_type, name }. Max 20 MB.addons — Extra checks for this candidate. Send keys in lowercase (e.g. employment, not EMPLOYMENT).candidate.meta_data — Free-form JSON echoed back unchanged in GET /details and the webhook. Use it to carry your own reference.Response: data.candidate_id — use this for submit and GET /details.
Status codes: 200 created · 400 validation / invalid subtype / insufficient credits · 409 duplicate candidate · 413 résumé too large.
3. POST /external/v2/candidate/submit-bgv
Submits the candidate's check data. On success, SpringVerify creates every check the package requires and moves the candidate out of Awaiting Input.
Checks are keyed objects: identity_1, employment_1, education_1, current / permanent for addresses. Each entry may carry a documents array of either hosted URLs or inline base64 files.
The payload is validated against a strict allow-list. An unknown key is rejected, not ignored. Nothing is written until the whole payload passes; a failed request leaves the candidate exactly as it was and you can correct and resend.
Key sections:
candidate_id (required)basic_details — full_name, dob, gender, mobile_number, uan_number, etc.identity — keyed identity_1, identity_2, ... Each has id_type (PAN / AADHAAR / PASSPORT / DL / VOTER_ID), id_number, name_on_document, documents[].employment — keyed employment_1, etc. Includes company_name, designation, start_date / end_date, hr_info, manager_info, documents[].education — keyed education_1, etc. Includes course_type, degree, university_name, documents[].address — keyed current, permanent, other_1, etc. Do not send a type inside the entry — the type comes from the key. Latitude and longitude are strings.social_media — linkedin_url, facebook_url, twitter_url, instagram_url.consent — { "doc_url": "https://..." }. Without it, the candidate is held in Consent missing.Status codes: 200 submitted · 400 validation failure (every problem listed in errors[]) · 409 already submitted.
4. GET /external/v1/candidate/details
Returns a candidate's status and per-check detail. Provide either a single identifier (candidate_id, candidate_uuid, or email) or limit and offset.
Useful query parameters:
include_check_status=1 — Include each check's check_status and check_status_code.include_check_details=1 — Add per-check date fields and verifier comments.include_check_remarks=1 — Add the insufficiency note (requires company setting enabled).include_documents_url=1 — Add pre-signed download links for each document (expires in 20 minutes).include_documents_base64=1 — Embed document content inline as base64 (capped at 7 MB total).report_type=base_64 — Also return the report PDF inline as base64.overall_status_code values: 0 In progress · 1 Completed · 3 Awaiting Input · 4 Processing · 5 Discrepancy · 6 Completed with exception · 8 Closed · 9 On Hold · 10 Cancelled · 11 Consent missing · 12 Insufficient funds.
A candidate is finished at codes 1, 6, 8, or 10. Stop polling then.
check_status_code values: 0 Ready to initiate · 1 Verified · 2 Unable to Verify · 4 In Progress · 5 On Hold · 7 N/A · 9 Form submitted · -1 Insufficiency · -2 Major Discrepancy · -3 Minor Discrepancy.
5. GET /external/v1/candidate/report/pdf
Downloads the completed BGV report.
curl --location 'https://api-sa.in.springverify.com/external/v1/candidate/report/pdf?candidate_id=123456' \ --header 'Authorization: Bearer <YOUR_API_TOKEN>' \ --output report.pdf
Add &report_type=base_64 to get JSON with a base64-encoded PDF instead of a binary stream.
Async generation for large reports:
POST /external/v1/candidate/report/pdf/initiate with { "candidate_id": 123456 } — returns a job_id.
Poll GET /external/v1/candidate/report/pdf/status?job_id=<uuid> until data.status is completed.
On completed, data.downloadUrl holds a pre-signed link (expires — download promptly).
Webhook report push
Instead of polling, SpringVerify can POST to your endpoint when a candidate reaches a final status. The payload includes: event, candidate_id, candidate_uuid, overall_status, overall_status_code, name, email, employee_id, report_url (signed, expires), completed_date, and meta_data.
event: "completed". Interim pushes can be enabled.overall_status_code, not overall_status (labels are inconsistently cased).Error handling
{ "msg": "...", "description": "..." }.Known limitations
category_id on POST /add — Accepted but not returned by GET /details. Use tags or carry the value in meta_data.employment).skipped_checks as an array — Ignored. Send an object keyed by check name: { "employment_check": 1 }.Testing & sandbox
A sandbox is available so you can build and test before going live. Request sandbox credentials from your SpringVerify point of contact. Sandbox checks are not run — statuses are moved manually by the backend. No charges apply.
When you go live, switch to the production host and token, then re-read GET /packages (ids differ between environments).
Migrating from older field names
An earlier version of the API used different field names. These are still translated for now but will be withdrawn:
files[{ original_file_url, document_tag }] → documents[{ url, tag }]basic_details.phone_number → basic_details.mobile_numberidentity.identity_candidate_name → identity.name_on_documentemployment.company_legal_name → employment.company_nameaddress.<entry>.type → remove it; the type comes from the keylatitude / longitude as numbers → send as stringsFor the full Postman collection, see documenter.getpostman.com.