DEVELOPER GUIDE / API v1
MRZ tools.
Your workflow.
Validate document text, create synthetic records, and export test batches through one API.
1. Start the server
From the kyc folder, use Node.js 22.17 or newer:
npm ci
npm run build
npm start Your base URL is http://127.0.0.1:4173/api/v1. Open this
tutorial at http://127.0.0.1:4173/api.html. Local development needs no key by default.
2. Make your first request
PowerShell: generate a French ID test record and validate its MRZ.
$base = 'http://127.0.0.1:4173/api/v1'
$body = @{ profileId = 'fr-id'; seed = 'tutorial-1' } | ConvertTo-Json
$record = Invoke-RestMethod "$base/generate" -Method Post -ContentType 'application/json' -Body $body
$record.mrz
$body = @{ profileId = $record.profile; mrz = $record.mrz } | ConvertTo-Json
$result = Invoke-RestMethod "$base/validate" -Method Post -ContentType 'application/json' -Body $body
$result.status # pass macOS / Linux curl:
curl http://127.0.0.1:4173/api/v1/profiles
curl http://127.0.0.1:4173/api/v1/generate \
-H 'Content-Type: application/json' \
-d '{"profileId":"fr-id","seed":"tutorial-1"}' A generated record includes mrz, lines, input, profile, seed, synthetic: true, and expected/actual outcomes. The same
seed and options produce the same record.
3. Try a request
This sends a synthetic example to this page’s SvelteKit API.
4. Automate the full workflow
The included Node example discovers profiles, generates and validates a record, imports its fields, then saves a three-record CSV file:
node examples/api-demo.mjs For Python, use the standard library—no extra packages required:
import json
import os
import urllib.request
BASE = "http://127.0.0.1:4173/api/v1"
def post(endpoint, payload):
headers = {"Content-Type": "application/json"}
if os.getenv("API_KEY"):
headers["Authorization"] = "Bearer " + os.environ["API_KEY"]
request = urllib.request.Request(
BASE + "/" + endpoint,
data=json.dumps(payload).encode("utf-8"), headers=headers)
with urllib.request.urlopen(request, timeout=30) as response:
return json.load(response)
record = post("generate", {"profileId": "fr-id", "seed": "tutorial-1"})
result = post("validate", {"profileId": record["profile"], "mrz": record["mrz"]})
print(result["status"])Endpoint reference
Prefix all paths with /api/v1. POST bodies use Content-Type: application/json. Responses are JSON except
CSV/TXT exports. Download the OpenAPI specification to import
into an API client.
| Method / path | Input and result |
|---|---|
| GET /health | Server status and API version. |
| GET /profiles | {profiles: [...]} with IDs, editions, country,
format, number patterns and sources. |
| GET /profiles/{profileId} | One profile, for example /profiles/fr-id. |
| GET /metadata | Accepted input fields, country codes, failure modes and limits. |
| GET /geography/france | {departments: [...]} with bundled sample cities
and postcodes. |
| POST /dummy | Required profileId; optional seed, info overrides. Returns {profileId, seed, synthetic, info}.
Document and national numbers are generated; do not include them
as overrides. |
| POST /generate | Required profileId; optional seed, failure, info. Returns one record.
Omit info for full dummy data; supplied info is used as-is. |
| POST /batch | Same input as generate plus count (1–1000, default
10). Returns {schemaVersion, synthetic, count, records}. Preserves identity details and varies document/national
numbers, including supplied numbers. |
| POST /validate | Required mrz; optional profileId (default auto), today (YYYY-MM-DD; default UTC today).
Returns fields, spans, check groups, warnings, candidates, valid and
status. |
| POST /import | Same request as validate. Returns {profileId, info, notes} for generation. Review date-century assumptions; supplementary details
are blank because MRZ does not encode them. |
| POST /parse | Required mrz. Returns detected layout, lines,
fields, spans and check definitions. Does not validate them. |
| POST /checksum | Required text using A–Z, 0–9 and <. Returns {digit, explanation}. Example: {"text":"L898902C3"} returns digit 6. |
| POST /export | Required records (1–1000 generated record
objects), format (json/csv/txt). Returns a download attachment.
TXT contains MRZ only; JSON/CSV preserve full details. |
Custom fields and intentional failures
First call /dummy, edit its info, then pass
that complete object to /generate. All info values are
strings, including postal codes, height and CAN. Fields include surname,
givenNames, birthDate, expiryDate, sex (M/F/<), nationality,
documentNumber, nationalNumber, optional1 and optional2. /metadata lists all address, birthplace and issuing-office fields.
Dates use YYYY-MM-DD. Nationality uses ICAO codes such as FRA.
Country-specific identifiers must match the document profile. If you
change birth date or sex for a profile with dependent national
identifiers, request new /dummy data with those overrides first.
Additional fields are rejected to catch spelling mistakes.
The default seed is mrz-lab. To isolate an expected
failure, use failure: checksum, character, length, or date. The
default valid creates a passing record.
{"profileId":"fr-id","seed":"negative-test","failure":"checksum"} An invalid MRZ is still a successful validation request: HTTP 200 with status: "fail". partial means core checks passed but a document profile
needs selection. Inspect status and groups; valid alone does
not guarantee a fully selected profile or authenticity.
API keys and deployment
To enable authentication in PowerShell, set a secret before starting:
$env:API_KEY = 'replace-with-a-long-random-secret'
npm start Include Authorization: Bearer YOUR_KEY in every API
request. For the PowerShell examples, add -Headers @{ Authorization = "Bearer $env:API_KEY" }. The Node and Python examples read API_KEY automatically.
Documentation and static pages stay publicly readable.
To accept connections beyond your computer, set HOST=0.0.0.0 and an API_KEY. Set PORT if needed. Run this Node project on
a server behind HTTPS; the SvelteKit Node or Vercel adapter runs the API alongside
the UI. Add request-rate limits at your reverse proxy before public exposure.
A 1,000-record batch runs synchronously, so heavy concurrent use needs capacity
planning.
For browser clients on other sites, set CORS_ORIGINS to a
comma-separated list of exact origins, for example https://your-app.example. CORS controls browser access; it
does not replace API-key authentication. Keep shared keys on trusted
servers rather than embedding them in public web apps.
The server stores no MRZ records and does not log request bodies. Configure any hosting proxy’s logs accordingly. Generated numbers can coincide with assigned numbers; use these outputs as synthetic test fixtures.
Errors and limits
Errors have the shape {"error":{"code":"INVALID_REQUEST","message":"…"}}.
- 400: malformed JSON, missing fields, unknown profile, invalid type or option.
- 401: missing or incorrect API key. 403: browser origin is not allowed.
- 404: unknown endpoint or profile. 405: wrong HTTP method.
- 413: body exceeds 2 MiB. 415: missing application/json content type.
- 422: incompatible generator values or fields that cannot be imported.
- 500: unexpected server failure.
Limits: 1,000 records per batch/export; 4,096 characters per MRZ/checksum input; 500 characters per info value; 200 characters per seed. Large batches with unusually long details may need smaller chunks to fit the export request limit.