Type values below are the detection entity types: exactly
what /mask reports in spans[].type and the
<MASKED_{TYPE}_…> token. /redact uses a slightly different public
placeholder prefix for some of them (e.g. PERSON → PERSON_NAME);
see Placeholder shape.CUSTOM_TERM. The On by
default column is what a new account starts with; see
Which types are on by default.| Type | On by default | What it catches | Latin example | Arabic example |
CUSTOM_TERM | always | Caller-defined glossary terms (codenames, aliases, contract IDs) | Project Falcon | مشروع الصقر |
PERSON | ✅ | Latin + Arabic names | Mohammed bin Salman | محمد بن سلمان |
NATIONAL_ID | ✅ | 10-digit NID, leading 1, checksum-validated | 1012345672 | ١٠١٢٣٤٥٦٧٢ |
RESIDENCE_PERMIT | ✅ | 10-digit iqama, leading 2, checksum-validated | 2012345670 | — |
PHONE_NUMBER | ✅ | KSA mobile + international | +966 50 123 4567 | ٠٥٠١٢٣٤٥٦٧ |
EMAIL_ADDRESS | ✅ | RFC 5321 | salem@example.sa | — |
DATE_TIME | ✅ | Dates, times, durations | 2026-05-27, Jan 1 1980 | ٢٠٢٦/٠٥/٢٧ |
IBAN | ✅ | 24-char Saudi IBAN, mod-97 validated | SA03 8000 0000 6080 1016 7519 | — |
CREDIT_CARD | ✅ | 14–16 digit PANs, Luhn-validated, mada BIN table for network tagging | 4111 1111 1111 1111 | ٤١١١ ١١١١ ١١١١ ١١١١ |
PASSPORT | ✅ | ICAO MRZ format | A12345678 | — |
ADDRESS | ✅ | REDF + short-code addresses | 7821 King Fahd Rd | ٧٨٢١ طريق الملك فهد |
ORGANIZATION | — | Saudi organizations | Saudi Aramco | أرامكو السعودية |
LOCATION | — | Cities, regions, countries | Riyadh | الرياض |
MRN | — | Hospital medical record number | H123456, MRN: 0012345 | — |
BUSINESS_ID | — | Commercial registration (context-gated) | CR 1010xxxxxx | سجل تجاري ١٠١٠xxxxxx |
TAX_ID | — | ZATCA zakat number, leading 3 (shape-only, no public check digit) | 3xxxxxxxxx | — |
VEHICLE_PLATE | — | Strict 17-letter Latin + Arabic plate alphabets | أ ب ج 1234 | أ ب ج ١٢٣٤ |
STUDENT_ID | — | Student ID (context-gated) | Student ID 4400123456 | — |
INSURANCE_POLICY | — | Insurance policy ID (context-gated) | Policy POL-987654 | — |
MEDICAL_LICENSE | — | Saudi MOH / SCFHS practitioner license numbers | MOH-123456, SCFHS-987654 | — |
API_KEY | — | Provider-prefixed secrets (Stripe, AWS, GitHub, …). Prefix-anchored only; KINDI deliberately does not flag arbitrary high-entropy strings | sk_live_…, AKIA…, ghp_… | — |
MONETARY_AMOUNT | — | Amounts in 18 currencies, symbol / ISO-code / word forms, incl. Arabic | SAR 12,500, $4,300 | ١٢٥٠٠ ريال |
SSN | — | US-format social security numbers | 123-45-6789 | — |
IP_ADDRESS | — | IPv4 + IPv6 literals | 203.0.113.42 | — |
ORGANIZATION / MRN / MONETARY_AMOUNT".PERSON, NATIONAL_ID, RESIDENCE_PERMIT,
PHONE_NUMBER, EMAIL_ADDRESS, DATE_TIME, IBAN, CREDIT_CARD,
PASSPORT, ADDRESSORGANIZATION, LOCATION, MRN, BUSINESS_ID,
TAX_ID, VEHICLE_PLATE, STUDENT_ID, INSURANCE_POLICY,
MEDICAL_LICENSE, API_KEY, MONETARY_AMOUNT, SSN, IP_ADDRESSPUT /me/entities. CUSTOM_TERM is never
filtered by the toggles; a glossary term always
masks.| Type | Check |
NATIONAL_ID / RESIDENCE_PERMIT | Saudi position-doubling Luhn variant over all 10 digits |
IBAN | ISO 13616 mod-97 |
CREDIT_CARD | Standard mod-10 Luhn (unconditional; this one is never relaxed) |
1012345678 or a random 16-digit string, is not masked. This is
deliberate; checksum gating is what keeps precision high on 10-digit
numbers that are really order references, timestamps, or part numbers. If
you're building a test fixture, generate checksum-valid values (or use the
examples in the table above) or your test will look like a detection miss.
TAX_ID has no authoritative public check digit and stays shape-only.Cf — zero-width spaces and joiners, bidi embeds,
isolates and marks, soft hyphen, BOM, Arabic format marks) and invisible
variation selectors, and folds fullwidth digits (123) to ASCII. Without
this, a single zero-width space pasted inside an ID from a PDF or an RTL
document defeats every digit pattern and the value flows through unmasked.
Offsets in spans are mapped back to your original text, so this is
invisible to you.| Endpoint | Token shape | Example | Notes |
POST /api/v1/mask | <MASKED_{TYPE}_{8-hex}> | <MASKED_PERSON_a1b2c3d4> | {TYPE} is the detection entity type from the catalogue above. One token is minted per distinct (type, surface text) pair and reused for every repeat occurrence — the same name appearing three times yields the same token three times, and one entry in the mappings object. Two different people both named in the text get two different tokens. The encrypted envelope maps each token back to its original. |
POST /api/v1/redact | {PREFIX}_{NN} | PERSON_NAME_01 | {PREFIX} is the public placeholder prefix (see mapping below). Enumerated per type, starting at _01. Repeated values within a single request share the same number; numbering restarts at _01 on the next request. No envelope, no recovery. |
/mask keeps the raw detection type; /redact renames a handful of
types to a friendlier public placeholder. Most types are identical
across both endpoints; only these differ:Detection type (/mask) | Redact placeholder (/redact) |
PERSON | PERSON_NAME |
PHONE_NUMBER | PHONE |
EMAIL_ADDRESS | EMAIL |
DATE_TIME | DATE |
ORGANIZATION | ORG |
CREDIT_CARD | CARD |
IP_ADDRESS | IP |
MONETARY_AMOUNT | AMOUNT |
CUSTOM_TERM, NATIONAL_ID, RESIDENCE_PERMIT,
PASSPORT, IBAN, BUSINESS_ID, TAX_ID, MRN, ADDRESS,
LOCATION, VEHICLE_PLATE, INSURANCE_POLICY, STUDENT_ID,
MEDICAL_LICENSE, API_KEY, SSN) is spelled the same in both.12mask → /<MASKED_([A-Z_]+)_([a-f0-9]{8})>/g redact → /([A-Z_]+)_(\d{2,})/g
| Rank | Types |
| 110 | CUSTOM_TERM — your glossary terms beat every recognizer and the NER, and bypass the confidence filters |
| 100 | NATIONAL_ID |
| 95 | RESIDENCE_PERMIT, IBAN |
| 90 | PASSPORT, PERSON |
| 85 | ADDRESS |
| 80 | EMAIL_ADDRESS, TAX_ID |
| 75 | VEHICLE_PLATE |
| 70 | PHONE_NUMBER |
| 65 | BUSINESS_ID |
| 60 | MEDICAL_LICENSE, INSURANCE_POLICY, STUDENT_ID, API_KEY |
| 55 | MRN |
| 50 | CREDIT_CARD, SSN |
| 35 | MONETARY_AMOUNT |
| 30 | DATE_TIME |
| 20 | LOCATION |
| 10 | ORGANIZATION |
| 5 | IP_ADDRESS |
NATIONAL_ID should mask as such, not as a generic PHONE_NUMBER, and a
date inside an address shouldn't split the address in two.