≪ MRZ lab

DEVELOPER GUIDE / API v1

MRZ tools.
Your workflow.

Validate document text, create synthetic records, and export test batches through one API.

The HTTP API runs in SvelteKit server routes. API requests send their input to the server; the main browser workspace processes data locally.

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.

Ready

Your response will appear here.

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 / pathInput and result
GET /healthServer 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 /metadataAccepted input fields, country codes, failure modes and limits.
GET /geography/france{departments: [...]} with bundled sample cities and postcodes.
POST /dummyRequired profileId; optional seed, info overrides. Returns {profileId, seed, synthetic, info}. Document and national numbers are generated; do not include them as overrides.
POST /generateRequired profileId; optional seed, failure, info. Returns one record. Omit info for full dummy data; supplied info is used as-is.
POST /batchSame 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 /validateRequired mrz; optional profileId (default auto), today (YYYY-MM-DD; default UTC today). Returns fields, spans, check groups, warnings, candidates, valid and status.
POST /importSame request as validate. Returns {profileId, info, notes} for generation. Review date-century assumptions; supplementary details are blank because MRZ does not encode them.
POST /parseRequired mrz. Returns detected layout, lines, fields, spans and check definitions. Does not validate them.
POST /checksumRequired text using A–Z, 0–9 and <. Returns {digit, explanation}. Example: {"text":"L898902C3"} returns digit 6.
POST /exportRequired 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.