Product Guide 14
Fintech AI

Government ID Masking: How to Build It

A step-by-step guide to masking and vaulting government identity numbers in India. Eight stages, the options at each one, exactly how each step connects to the next, real costs, and what breaks. Written for someone who has not built this before.

Verified September 2026Free · No signupOfficial sources only
BeginnerStart here. No prior knowledge assumed.

How to use this page

This page walks you through building one thing: making sure a government identity number stops existing in your systems once you no longer need it, while the document it came from remains usable.

Every other product guide on this site is about collecting something. This one is about getting rid of it. That inversion changes the whole shape of the work — there is no conversion funnel to optimise and no revenue attached, and the failure is invisible until somebody looks.

Watch out

The product almost everyone builds is the visible 10% of the obligation. Teams hear “Aadhaar masking” and build image redaction: detect the number on a scan, black out the first eight digits, store the masked copy. That part is real and required. It is also the easy part. The harder requirement is that the Aadhaar number must not sit in your business databases at all — it belongs in a separate encrypted vault, referenced everywhere else by a token. A team that ships beautiful redaction and keeps the number in a customer table has done the visible part and missed the obligation.

Masking and vaulting are two different duties

Two separate duties, frequently confused, with different sources and different failure modes:

MaskingVaulting
What it isObscuring the number on the document imageKeeping the number out of your databases
Who requires itRBI, IRDAI, SEBI for their regulated entitiesUIDAI, for anyone storing Aadhaar numbers
What good looks likeFirst eight digits and the QR code obscuredNumber only in the vault; a Reference Key everywhere else
How it failsVisibly — a reviewer sees the numberInvisibly — until an audit or a breach
Note

The regulatory spine, and it is four bodies rather than one. UIDAI's 2018 circular established that a masked Aadhaar is valid proof of identity. RBI's amendment of 29 May 2019 to the KYC Master Direction requires regulated entities to redact the number where authentication is not required. IRDAI followed on 29 January 2019 and SEBI on 24 April 2020. Underneath all of them sits the Aadhaar Act, 2016 and the Supreme Court's 2018 judgment, which upheld the scheme subject to conditions on how the number may be held and used.

What this is not:

  • Not an OCR project. Detection is a solved problem and the least of your difficulties. Steps 4 to 8 are the work.
  • Not only about Aadhaar. PAN, passport, voter ID and driving licence numbers are personal data under the DPDP framework even without a dedicated vault rule.
  • Not finished when the pipeline ships. The number is already in places nobody listed. Step 6.

The whole journey, in one table

#StepIn plain words
1Find out what you holdBefore designing anything. It is more places than the list says.
2Decide what you may keepThe cheapest number to protect is one you never stored.
3Mask at captureFirst eight digits and the QR. The visible layer.
4TokeniseThe Reference Key. The step that is actually the product.
5Build the vaultSeparate, encrypted, isolated, keys in an HSM.
6Purge where it leakedLogs, tickets, backups, video frames. The hard one.
7Serve it backFor the few cases that genuinely need the real number.
8Prove itAccess logs, retention, and evidence that survives a question.
Note

Steps 1, 6 and 8 are the ones that get skipped, and they are the ones that decide whether any of this worked. Steps 3 to 5 are the part that looks like a project and a vendor will sell you all three. Nobody sells you the discovery or the purge, because both are unglamorous and specific to your estate.

IntermediateBuild it. Pipelines, tools and working code.

Steps 1 and 2 — what you hold, and what you may keep

Step 1 — Find out where the number already is

Start with an inventory, not an architecture. In every organisation that has been operating for more than a year, the answer is longer than the application diagram suggests.

The places to look, in roughly the order teams are surprised by them:

  • The obvious ones: the customer table, the KYC document store, the onboarding database.
  • Application logs. A request body logged at debug level, an error trace with the payload attached, a third-party logging service that has been retaining it for two years.
  • The support system. Customers send their Aadhaar as an attachment; agents paste numbers into ticket notes. This is almost always the largest uncontrolled store.
  • Email. Documents sent to an operations mailbox, sitting in an archive.
  • Analytics and monitoring, where a form field was captured as an event property.
  • Backups and data warehouses, which hold every version of the table you are about to clean.
  • Video KYC recordings, where the customer held the card up to the camera.
Watch out

The video case is the one that is most often missed and hardest to fix. A V-CIP session where the customer displays their Aadhaar produces a stored recording with the number legible in individual frames. Redaction obligations follow the number, not the file format — a number visible in a stored frame is a stored Aadhaar number. Frame-level detection and redaction on video is materially harder than on a scan, and the recordings usually have a long mandated retention. If you run Video KYC, put this on the plan at the start rather than discovering it in an audit.

Step 2 — Decide what you are entitled to keep

The cheapest number to protect is the one you never stored. Before building a vault, work through what you actually need.

Do you need the number, or do you need the verification? For most onboarding journeys the answer is the second: you need to know the identity was verified, not to retain the identifier afterwards. Where that is true, store the verification result and the reference, and discard the number entirely.

Do you need the document image, or the extracted fields? Name, date of birth and address often suffice, in which case the image can go.

What is your retention period, and who set it? “Indefinite” is a decision, usually one nobody made. Under the DPDP framework personal data is not to be kept beyond the purpose it was collected for, and there is no legitimate-interest basis in India to fall back on.

Steps 3 to 5 — mask, tokenise, vault

Steps 3 to 5 — Mask, tokenise, vault

Python — steps 3 to 5, mask, tokenise, vault
from dataclasses import dataclass

# STEP 3. MASK AT CAPTURE. THE VISIBLE LAYER.
# The requirement is specific: the first EIGHT digits, and the QR code, which
# encodes the full number and is the part teams forget. Last four stay
# visible; name, date of birth, gender and address stay visible.

def mask_document(image):
    regions = detect(image)                    # number, QR, and any repeats
    assert regions["qr"], "QR not located — do not store this image"
    out = image
    for r in regions["number"]:                # a card may show it twice
        out = redact(out, r, keep_last=4)
    out = redact(out, regions["qr"], keep_last=0)
    verify = extract_digits(out)               # read your own output back
    assert not verify["full_number_visible"], "masking did not take"
    return out

# STEP 4. TOKENISE. THIS IS THE ACTUAL PRODUCT.
# UIDAI Circular No. 14 of 2025: every business system stores a REFERENCE KEY
# instead of the number. The actual number must not be stored in any business
# database other than the Aadhaar Data Vault.
#
# The reference key must not computationally permit working back to the
# number. So: a random token, not a hash of the number, and not a derivation
# from it -- a hash is guessable across a 12-digit space.

@dataclass
class Reference:
    key: str            # random, opaque, stored everywhere
    created_at: str

def tokenise(aadhaar, vault):
    assert len(aadhaar) == 12 and aadhaar.isdigit()
    existing = vault.lookup_by_number(aadhaar)     # inside the vault only
    if existing:
        return existing
    ref = Reference(key=random_token(32), created_at=now_ist())
    vault.store(aadhaar=aadhaar, reference=ref)    # the ONLY place both meet
    return ref

# Everything downstream stores ref.key. Nothing downstream stores aadhaar.
def create_customer(db, profile, ref):
    assert "aadhaar" not in profile, "number reached the business layer"
    return db.insert({**profile, "id_reference": ref.key})

# STEP 5. THE VAULT.
VAULT_REQUIREMENTS = {
    "separate_store": True,        # not a column on an existing table
    "single_logical_instance": True,
    "encryption": "AES-256_or_higher",
    "keys_in_hsm": True,           # and the HSM is not shared with anyone else
    "network": "restricted_zone_isolated_from_other_internal_zones",
    "access": "internal_systems_only",
    "ha_dr": True,
    "access_logged": True,
}

# WHAT TO CHECK
# [ ] the QR is masked, not just the digits. It encodes the whole number
# [ ] read your own masked output back and assert the number is gone. A
#     redaction that draws a box without removing the underlying pixels is
#     not a redaction, and some libraries do exactly that
# [ ] the reference key is RANDOM. A hash of a 12-digit number is brute
#     forceable in seconds
# [ ] assert at the boundary that no business write contains the number.
#     Make it fail the request, not log a warning
# [ ] the HSM is not shared with another legal entity. Where a group shares
#     one, each entity needs logical isolation and its own crypto keys
# [ ] the vault is one logical instance, not one per service

THE finding, and it is the reason this page exists: masking the image is the visible tenth of the obligation. The requirement is that the number stops existing in your business systems.

UIDAI's framework has been in place since a first circular in July 2017 and was substantially revised by Circular No. 14 of 2025, dated 18 July 2025, with updated FAQs on 3 November 2025. Its central instruction is unambiguous: all systems requiring storage of Aadhaar numbers should maintain only the reference key, and the actual number should not be stored in any business database other than the Aadhaar Data Vault.

Alongside it: the vault as a single logical instance per entity, encryption at AES-256 or higher, encryption keys held in an HSM that is not shared with any other legal entity, the whole thing in a restricted network zone isolated from other internal zones, accessible through internal systems only, with high availability and disaster recovery.

Note

A group structure does not let you share the HSM freely. Where a sub-entity uses its parent's HSM, the configuration must provide logical isolation and dedicated crypto keys for each regulated entity, and the parent must not be able to read the sub-entity's vault. This is the detail most often got wrong in a shared-services model, because sharing infrastructure is exactly what a shared-services model is for.

Note also what belongs in the vault: not only the number, but the e-KYC XML, the Aadhaar PDF returned in an e-KYC response, and the related demographic data. A team that vaults the number and leaves the e-KYC response in object storage has moved the problem rather than solved it.

Steps 6 to 8 — purge, serve back, prove

Steps 6 to 8 — Purge, serve back, prove

Python — steps 6 to 8, purge, serve back, prove
# STEP 6. PURGE WHERE IT ALREADY LEAKED. THE UNGLAMOROUS ONE.
# A clean pipeline from today does nothing about the last three years.

PURGE_TARGETS = [
    "application_logs", "error_traces", "third_party_log_retention",
    "support_tickets", "ticket_attachments", "operations_mailbox",
    "analytics_event_properties", "data_warehouse", "backups",
    "vcip_recordings", "developer_laptops_and_exports",
]

def purge_plan(target, scanner):
    hits = scanner.find_id_numbers(target)      # pattern + checksum, not regex alone
    return {
        "target": target,
        "found": len(hits),
        # Deleting a support ticket may break an audit trail you are separately
        # required to keep. Redact IN PLACE where the record must survive.
        "action": "redact_in_place" if target in ("support_tickets", "vcip_recordings")
                  else "delete",
        "backups_note": "a purge that skips backups restores the problem on the "
                        "next restore -- schedule it against the retention cycle",
    }

# STEP 7. SERVE IT BACK, FOR THE FEW CASES THAT NEED IT.
def resolve(reference_key, purpose, actor, vault):
    # Every de-tokenisation is an event with a reason and a name attached.
    assert purpose in ("regulatory_filing", "authentication", "lawful_request")
    entry = vault.read(reference_key, actor=actor, purpose=purpose)
    vault.log_access(reference_key, actor, purpose, at=now_ist())
    return entry            # held in memory, never written downstream

# STEP 8. PROVE IT.
def evidence(period, vault, scanner):
    return {
        "access_log": vault.accesses(period),        # who, what, why, when
        "unresolved_scan_hits": scanner.sweep(PURGE_TARGETS),
        "retention_expiries_actioned": vault.expired_and_deleted(period),
        "hsm_key_rotation": vault.key_events(period),
        "masking_sample_reverified": sample_and_recheck(period, n=50),
    }

# WHAT TO CHECK
# [ ] run the scanner against your OWN systems on a schedule, not once. New
#     leaks appear with every feature that logs a request body
# [ ] validate with the checksum, not the pattern. Any 12 digits matches a
#     naive regex, and a phone number plus two digits is not an Aadhaar
# [ ] purge backups against the retention cycle, or the next restore undoes
#     the work
# [ ] every de-tokenisation is logged with an actor and a purpose. An access
#     log with no purpose column cannot answer the question that gets asked
# [ ] re-verify a sample of masked documents periodically. Pipelines drift,
#     and a model update can change what gets detected
# [ ] timestamp everything in IST. An access log in UTC is read by a
#     regulator in IST, and a five-and-a-half hour offset on an access
#     timestamp is exactly the detail that turns a routine question awkward

The second finding, and the one that costs the most: a clean pipeline from today does nothing about the last three years. The number is already in your logs, your support tickets, your operations mailbox, your warehouse and your backups, and it got there through ordinary engineering decisions that nobody would call a mistake at the time.

Two details that decide whether the purge works. Validate with the checksum rather than the pattern — a naive twelve-digit regex matches phone numbers, order ids and timestamps, and a scan that returns thousands of false positives is a scan nobody finishes. And schedule the backup purge against the retention cycle, because a restore from an unpurged backup puts everything back.

Watch out

Redact in place where the record has to survive. Deleting a support ticket to remove an Aadhaar number can breach a separate obligation to retain the interaction. The same applies to V-CIP recordings, which carry their own mandated retention. These two duties — keep the record, remove the number — are simultaneous rather than alternative, and a purge designed as deletion will be stopped by compliance halfway through.

What it costs

Government ID masking — what it costs

Verified September 2026
Discoverydirect
The step nobody sells and nobody budgets. Scanning logs, tickets, mailboxes, warehouses and backups across your estate. Expect it to find more than the architecture diagram suggests, and expect the support system to be the largest store.
Maskingdirect
Per document, or a licence. The cheapest line on this list and the one that gets the attention, because it is the part that demonstrates well.
The vault and the HSMdirect
A separate encrypted store, keys in a hardware security module not shared with any other legal entity, in an isolated network zone, with high availability and disaster recovery. This is real infrastructure with real running cost.
Retrofitting the applicationindirect
Usually the largest line and never in the estimate. Every query, report, export and integration that currently reads the number has to be changed to read a reference key. On a system of any age this is months, not weeks.
The purgedirect
One-off across the historical estate, then a standing scheduled scan. Budget both; the second is the one that keeps it clean.
Video redactiondirect
If you run Video KYC, price this separately and early. Frame-level detection on recordings with long mandated retention is materially harder and more expensive than redacting a scan.
Getting it wrongindirect
A KYC violation can attract penalties that accrue per day for as long as it continues, and Indian regulators have imposed penalties in the crores on banks and NBFCs for systematic KYC failures. Alongside that sits DPDP exposure reaching ₹250 crore.
Where to buy these: Identity Onboarding Build Sheet names every tool with its unit cost. Getting Access covers which ones you can sign up for today, which need a sales call, and which are licensed.
Note

The number worth computing before anything else: how many systems currently read the identity number? That count is your retrofit cost, it is knowable in an afternoon, and it is almost always the figure that turns a three-month plan into a nine-month one. Everything else on this list is smaller and better understood.

AdvancedShip it. Failure modes, thresholds and evidence.

Three versions you could build

Do not store it at all

Build: verify identity → keep the verification result and a reference → discard the number and, where you can, the image.

You get: the obligation reduced to almost nothing. This is the correct answer far more often than it is chosen, and the reason it is not chosen is usually that nobody asked whether the number was needed after onboarding.

Mask and tokenise

Build: masking at capture including the QR → random reference key → encrypted vault with HSM-held keys in an isolated zone → a boundary assertion that fails any business write containing the number → a scheduled scan across logs, tickets and warehouse.

Trade: real infrastructure and a real retrofit, against an obligation that is not optional if you are regulated.

The full estate

Build: the above, plus the historical purge including backups, frame-level redaction on V-CIP recordings, de-tokenisation logged with actor and purpose, retention expiry actioned automatically, and periodic re-verification of masked output.

It breaks when: the project is scoped as the pipeline and the estate is treated as a later phase. The estate is the project; the pipeline is the part that stops it getting worse.

Note

If you take one thing from this page: assert at the boundary that no business write contains an identity number, and make it fail the request rather than log a warning. It is a few lines, it is the only control that holds as the system grows, and it turns an invisible failure into a visible one.

What goes wrong

What goes wrongWhyFix
Digits masked, QR left intactThe QR is not read as part of the number.It encodes the whole number. Mask it.
Redaction draws a box over the pixelsSome libraries overlay rather than remove.Read your own output back and assert.
Reference key is a hash of the numberIt looks opaque.A 12-digit space is brute forceable. Use a random token.
Number still in the customer tableMasking shipped; vaulting did not.Reference key everywhere; number only in the vault.
e-KYC XML left in object storageOnly the number was treated as in scope.XML, PDF and demographic data belong in the vault too.
Number in application logsA request body logged at debug level.Boundary assertion, plus a scheduled log scan.
Support tickets full of numbersCustomers attach documents; agents paste.Redact in place. The record may have to survive.
Purge undone by a restoreBackups were out of scope.Schedule against the retention cycle.
V-CIP recordings show the cardRedaction scoped to scans.The obligation follows the number, not the format.
Shared HSM across group entitiesShared services is the point of shared services.Logical isolation and dedicated keys per entity.
Scanner returns thousands of false hitsTwelve-digit regex with no checksum.Validate with the checksum. Otherwise nobody finishes the scan.
Access log with no purpose columnLogged the read, not the reason.Actor and purpose on every de-tokenisation.

Where to go next

Watch out

This page is a guide, not a specification. Handling Aadhaar data is governed by the Aadhaar Act and by UIDAI circulars that have been revised repeatedly, most recently in 2025. Nothing here is legal advice. Have your vault design, your retention periods and your purge plan reviewed by qualified counsel, and work from the current circular text rather than from this page.

Sources

Every figure, rule and date on this page, and where to check it. Entries are typed so you can see which are primary-sourced and which are industry reporting.

  1. officialUIDAI Circular No. 14 of 2025 on the Aadhaar Data Vault, dated 18 July 2025 — the requirement that all business systems store a Reference Key rather than the Aadhaar number, that the actual number not be stored in any business database other than the vault, the single logical instance per entity, AES-256 or higher encryption, keys held in an HSM, the restricted and isolated network zone, internal-systems-only access, and high availability and disaster recovery. Updated FAQs were published on 3 November 2025. This revises the original circular of 25 July 2017. uidai.gov.in
  2. officialUIDAI guidance on masked Aadhaar — the 2018 position that a masked Aadhaar showing only the last four digits is valid proof of identity for KYC, and the requirement that the first eight digits and the QR code be obscured. uidai.gov.in
  3. officialRBI Master Direction — Know Your Customer, as amended 29 May 2019 — the requirement that regulated entities redact or black out the Aadhaar number where authentication of the number is not required, before the document is filed or stored. www.rbi.org.in
  4. officialIRDAI circular of 29 January 2019 and SEBI circular of 24 April 2020 — the parallel obligations extending masked-Aadhaar acceptance and redaction duties to insurers and their intermediaries, and to securities-market intermediaries. irdai.gov.in
  5. officialAadhaar (Targeted Delivery of Financial and other Subsidies, Benefits and Services) Act, 2016 — the statutory basis for the scheme and the restrictions on holding, using and disclosing the Aadhaar number, as upheld with conditions by the Supreme Court in 2018. www.indiacode.nic.in
  6. officialDigital Personal Data Protection Act, 2023 and the DPDP Rules, 2025 — purpose limitation and storage limitation for identity numbers generally, the absence of a legitimate-interest basis, and penalties reaching ₹250 crore. Identity numbers other than Aadhaar — PAN, passport, voter ID, driving licence — are personal data under this framework even without a dedicated vault rule. www.meity.gov.in
  7. industryLegal commentary on the 2025 ADV circular — the practitioner reading of scope, of sub-entity use of a parent HSM with logical isolation and dedicated crypto keys, and of what must be held in the vault beyond the number itself — e-KYC XML, the Aadhaar PDF from an e-KYC response, and related demographic data. Interpretation, not the notified text.
  8. industryReporting on KYC enforcement — penalties accruing per day for a continuing KYC violation, and enforcement actions in the crores against banks and NBFCs for systematic KYC failures. Directional; confirm any figure against the order before relying on it.

Checked September 2026. The Aadhaar Data Vault circular was revised in July 2025 and its FAQs in November 2025 — verify the current text before designing to any provision here.

Ask an AI about this page

Opens your assistant with this page as the source, and a question rather than a summary. It will ask what you are building before it answers.

Nothing is sent from here. The link carries only this page’s title and address.