| Type | Check |
NATIONAL_ID / RESIDENCE_PERMIT | The Saudi position-doubling Luhn variant over all 10 digits |
IBAN | ISO 13616 mod-97 |
CREDIT_CARD | Standard mod-10 Luhn (always enforced) |
1012345678, fails its check digit and is
deliberately not masked. That is what keeps precision high on
10-digit numbers that are really order references, timestamps, or part
numbers. TAX_ID stays shape-only; there is no authoritative public
ZATCA check-digit specification.| Type | Checksum-valid example |
NATIONAL_ID | 1012345672 or 1052338942 |
RESIDENCE_PERMIT | 2012345670 |
IBAN | SA03 8000 0000 6080 1016 7519 |
/mask, /redact), where your input is exact and precision is the
goal. File redaction deliberately relaxes it: OCR
routinely mangles one digit of a scanned national ID or IBAN, and a
mangled-but-obviously-an-ID number is exactly what must not survive into
a shared document. Inside a file, an identifier-shaped number is
redacted even when its check digit fails.Cf): zero-width
spaces and joiners, bidi embeds/isolates/marks, the soft hyphen, the
BOM, and Arabic format marks, plus invisible variation selectors.123) to their ASCII equivalents,
one-for-one.start/end in spans is projected
back onto the exact bytes you sent, so text[start:end] always yields
the detected surface, invisible characters included. You never need to
pre-clean input, and you must not re-derive offsets from a cleaned copy
of your own./mask's
spans, at identical offsets: once under the canonical name and once
under an older legacy alias kept for backwards compatibility. That
dual-emit was retired in August 2026. spans now carries one entry per
detected entity, canonical names only, and these legacy names no longer
appear:| Canonical | Retired legacy sibling |
NATIONAL_ID | SAUDI_NATIONAL_ID |
RESIDENCE_PERMIT | IQAMA |
PASSPORT | SAUDI_PASSPORT |
IBAN | IBAN_SA |
BUSINESS_ID | CR_NUMBER |
TAX_ID | ZAKAT_NUMBER |
MRN | MRN_MEDICAL |
pii_count now equals len(spans). Prefer pii_count as the entity
count regardless; it has always been the contract.spans for a legacy name from the right-hand column,
switch to the canonical name.(start, end) and
keep the first entry./redact was never affected: its enumerated placeholders are
canonical-name only. See
Common mistakes #9
for the replay fix patterns and
the /mask reference
for the full contract.