Designing a Verified Badge Program: Validating Identity Claims for Millions of Applicants &mdash

Designing a Verified Badge Program

Designing a Verified Badge Program at Scale

A ground-up walkthrough of the system that reviews identity-verification applications, checks documents against real-world data, resists deepfakes and fraud, and issues a trusted “verified” badge — the kind of blue-check program used by social platforms, marketplaces, fintech, and professional networks.

01

Introduction & History

A “verified badge” is a small visual mark — usually a checkmark or seal — placed next to a person’s or organisation’s name on a platform. It tells every other user one simple thing: “we checked, and this account really belongs to the person or brand it claims to be.” That one small icon carries a lot of weight. It affects who people trust with their money, their votes, their attention, and sometimes their safety.

Verification programs are not new. Long before the internet, governments issued passports and driving licences, banks issued notarised signatures, and professional bodies issued licensing certificates — all early forms of “verified badges” for the physical world. What changed with the internet is scale. A newspaper in 1990 might verify a few hundred letters to the editor a year. A modern platform might receive millions of verification applications a month, from people in nearly every country, submitting photos of dozens of different document types, in dozens of languages, some genuine and some deliberately faked.

The first big wave of online verified badges appeared on social networks in the early 2010s, mostly to solve a narrow problem: stopping impersonation of celebrities and journalists. Over time the purpose expanded — verified badges now support marketplaces (proving a seller is a real registered business), professional networks (proving someone actually holds the job title they claim), dating apps (proving a profile photo matches a real, live person), and creator platforms (proving a payout account belongs to the content creator).

This tutorial builds, piece by piece, a production-grade system that can accept an identity verification application, run it through automated and human checks, detect fraud, and issue or reject a badge — reliably, at a scale of millions of applicants, while protecting extremely sensitive personal data.

Real-life analogy — think of airport immigration. A huge number of travellers land every day. Most go through an automated e-gate that scans their passport chip and face — fast, cheap, works for the vast majority of “obviously fine” cases. A smaller number get pulled aside to a human officer for closer questioning, because something looked slightly unusual. A tiny number get sent to a back room for a full investigation. A verified badge system works the same way: automated checks handle the bulk of traffic, humans handle the judgment calls, and specialists handle suspected fraud.
02

Problem & Motivation

Before designing anything, we need to be precise about the problem. “Verify millions of identities” sounds like one problem, but it is really a bundle of five different problems layered on top of each other:

  1. Identity proofing — does the document (passport, national ID, business registration certificate) look genuine, and does it match the person applying?
  2. Uniqueness — has this same real-world identity already been used to verify a different account (to stop one person collecting many badges, or badge-farming for resale)?
  3. Eligibility — does this applicant actually meet the platform’s criteria for a badge (e.g., “notable public figure,” “registered business,” “active creator with X followers”)?
  4. Fraud resistance — can the system resist forged documents, deepfake selfies, stolen identity photos, and bots submitting thousands of fake applications per hour?
  5. Fairness and appeals — can a wrongly rejected applicant find out why, correct a mistake, and re-apply, without needing to email a human directly?
Common Mistake

Many teams begin by asking “which face-matching vendor should we use?” That is jumping straight to implementation before defining the actual decision the system needs to make. The real question is: “given a bundle of evidence — document, selfie, metadata, account history — what is the probability this application is genuine and eligible, and what is our tolerance for false accepts versus false rejects?” Vendor selection is a detail that comes after that.

Why does this system deserve careful architecture rather than “just a form and an admin panel”? Three forces make it hard:

  • Volume and burstiness. Verification demand is rarely smooth. A platform announcement (“badges now open to everyone”) can cause application volume to jump 50x within hours. The system must absorb spikes without falling over or creating a backlog that takes weeks to clear.
  • Extremely sensitive data. Passport numbers, national ID numbers, biometric face data, home addresses — this is some of the most regulated personal data that exists. A breach here is catastrophic, both for users and for the company’s legal exposure.
  • Adversarial pressure. Unlike, say, a shopping cart system, this system has motivated attackers actively trying to defeat it: fraud rings buying stolen documents, deepfake generators producing synthetic selfies, and click-farms submitting fake applications to resell verified accounts.
i
What an Interviewer May Ask

“Why can’t you just use a third-party KYC (Know Your Customer) vendor and call it done?” A strong answer explains that vendors are usually good at document authenticity and face matching, but the platform still owns uniqueness checks against its own user base, eligibility rules specific to its product, fraud signals from account behaviour, appeals workflows, and data retention policy — none of which a generic KYC vendor can fully own for you.

2.1 Framing the Problem as a Decision System, Not a Form

It helps to mentally separate this system into two layers that are easy to blur together: a collection layer (gathering evidence from an applicant) and a decision layer (turning that evidence into a trustworthy yes/no outcome, with a clear explanation and an audit trail). Teams that focus all their early design effort on the collection layer — a nice-looking upload form — often end up bolting the decision layer on as an afterthought, which is backwards. The decision layer, including how confidently the system can say “we checked, and this is genuine,” is the actual product being built. The form is just the front door.

2.2 Defining Success Metrics Before Writing Code

Before any architecture diagram gets drawn, it is worth being explicit about what “success” means for this system, because different metrics pull the design in different directions. A team optimising purely for processing speed will lean heavily on full automation and accept a higher error rate. A team optimising purely for accuracy will lean on human review for a much wider band of cases and accept higher cost and slower turnaround. A team optimising for user trust and platform reputation will care disproportionately about the false-reject rate for genuine, high-profile applicants, since a single wrongly rejected notable figure can generate outsized negative attention. Naming these priorities explicitly, and often ranking them, is what allows later architectural trade-offs (Chapter 16) to be made consistently rather than case by case.

2.3 The Cost Asymmetry Between Errors

Not all mistakes cost the same. Wrongly approving a fraudulent application (a false accept) can enable real-world harm — a scammer impersonating a real business to defraud customers, for instance — and can also damage the platform’s credibility once discovered. Wrongly rejecting a genuine applicant (a false reject) mostly costs user trust and support burden, and is generally recoverable through an appeal. Because the two error types are not symmetric in impact, well-designed systems are usually tuned to be somewhat more cautious about false accepts than false rejects, even though this means a slightly higher rate of frustrating, correctable rejections for genuine users.

03

Core Concepts & Terminology

3.1 Identity Proofing

Identity proofing is the process of confirming that a claimed identity is real and belongs to the person presenting it. It usually happens in two parts: document verification (is this ID document authentic and unaltered?) and biometric matching (does the live selfie match the photo on the document?). Identity proofing answers the narrow question “is this a real identity, and is the applicant really that person,” which is deliberately kept separate from the broader question of whether that person should actually receive a badge — a distinction explored further in 3.6 and Chapter 4.

3.2 KYC (Know Your Customer)

KYC is a term borrowed from banking regulation, describing the process of verifying a customer’s identity before providing a service. Verified badge programs borrow the same techniques even outside finance, because the underlying question — “is this really who they say they are?” — is identical. Many of the design patterns used in a verified badge system — document authenticity checks, biometric matching, risk-based routing between automated and human review — were pioneered and refined inside banking and financial services long before social platforms and marketplaces adopted them for the very different purpose of protecting community trust rather than preventing money laundering, which is a useful reminder that this entire problem space did not originate with social media, and mature practices already exist to draw on.

3.3 Liveness Detection

Liveness detection checks that the selfie was taken from a real, live person in front of the camera right now — not a photo of a photo, a video replay, or a 3D mask. This defeats a very common and cheap attack: holding someone else’s printed photo or phone screen up to the camera.

Beginner example — a liveness check might ask the applicant to blink, turn their head slowly, or say a randomly generated number out loud while the front camera records a short clip. A static photo cannot do any of these things convincingly.

3.4 Document Forensics

Document forensics examines the pixel-level structure of an uploaded ID image: does the font match the government’s known template for that document type and year? Are the security features (holograms, microprint, UV patterns) present? Has any region been digitally edited (cloned pixels, inconsistent noise patterns, mismatched compression artefacts)?

3.5 Entity Resolution / Deduplication

Entity resolution is the process of deciding whether two records — for example, two separate applications — refer to the same real-world person. This matters enormously for a verified badge program, because a single stolen identity document could otherwise be used to badge multiple fraudulent accounts.

3.6 Risk Score

Rather than a single global yes/no rule, most production systems compute a numeric risk score (say 0–100) for each application, combining dozens of signals — document confidence, face-match confidence, account age, IP reputation, device fingerprint reuse, velocity of applications from the same source, and more. Thresholds on that score decide the routing: auto-approve, auto-reject, or send to a human reviewer.

3.7 Human-in-the-Loop (HITL) Review

No automated model is perfect, and regulators plus platform trust teams generally require that borderline or high-impact decisions get reviewed by a trained human before a final rejection or approval, especially where legal or reputational stakes are high.

Real-life analogy — think of a loan application at a bank. A credit-scoring model gives a fast automated score. Very strong applications get auto-approved, very weak ones get auto-declined, but the large middle band goes to a loan officer who reviews the file by hand. Verified badge systems follow the same three-lane pattern.

3.8 PII and Data Minimisation

PII is any data that can identify a specific individual — name, ID number, face biometric, address. Data minimisation is the principle of collecting and retaining only what is strictly necessary, for only as long as necessary, which is both a security best practice and, in many countries, a legal requirement.

3.9 Idempotency

Idempotency means that performing the same operation multiple times produces the same result as performing it once. It matters here because applicants on shaky mobile connections often double-tap “submit,” and retried background jobs (e.g., a document-scan worker that crashes and gets rescheduled) must not create duplicate applications or double-charge a verification fee.

3.10 False Accept Rate (FAR) and False Reject Rate (FRR)

These two numbers describe the two ways a verification system can fail. The False Accept Rate is the fraction of fraudulent or ineligible applications that the system wrongly approves. The False Reject Rate is the fraction of genuine, eligible applicants the system wrongly turns away. These two numbers move in opposite directions as you tighten or loosen thresholds — make the system stricter and FAR drops but FRR rises, and vice versa. Almost every design decision in this tutorial, from threshold tuning to human review bands, is really a decision about where to sit on this trade-off curve.

Beginner example — imagine a very strict airport security check that pats down every single traveller for ten minutes. It will almost never let a genuinely dangerous item through (low FAR), but it will make thousands of innocent travellers late for flights (high FRR). A verified badge system faces exactly the same tension, just measured in rejected genuine applicants instead of missed flights.

3.11 Step-Up Verification

Step-up verification means starting with a lightweight check and only asking for stronger evidence if something looks uncertain, rather than demanding the maximum level of proof from every applicant up front. For example, a first-time applicant might only need a document photo and a selfie; if the risk score lands in the uncertain middle band, the system can step up and request a live video call with a reviewer before making a final call.

3.12 Confidence Score vs Decision

It is important to separate a machine learning model’s raw confidence score (a number, say 0.87, describing how sure the model is that a document is genuine) from the final business decision (approve, reject, or review). The confidence score is one input among several; the decision is the output of applying business thresholds and rules to that score, combined with other signals. Conflating the two — treating “the model said 0.87” as automatically meaning “approve” — removes the flexibility to tune business risk appetite independently of model behaviour.

04

Architecture & Components

At a high level, the system is a pipeline: applicants submit evidence, the evidence flows through a series of automated checks, a risk score is produced, and the application is routed to an outcome — approved, rejected, or queued for human review. Around that pipeline sit supporting services for storage, notification, audit, and appeals.

Below is a description of each major component and the responsibility it owns.

4.0 Reading the Architecture Diagram

Before walking through each box individually, it helps to notice the overall shape of the diagram: a narrow funnel at the top (client apps, gateway, intake) widening into a parallel fan-out in the middle (the four verification checks running concurrently), narrowing back down through a single decision point, and then fanning out again at the bottom into issuance, notification, and audit. This funnel-fan-out-funnel-fan-out shape is a common pattern in decision-heavy systems: narrow where consistency matters most (a single application must get exactly one decision), wide where independent, parallelisable work happens (checks that don’t depend on each other’s output), and wide again where the outcome of that single decision needs to ripple out to several independent downstream consumers.

4.1 API Gateway

The single entry point for all client traffic. It terminates TLS, authenticates the requesting user, applies per-user and per-IP rate limits, and routes requests to the correct backend service. Because this system faces the open internet and handles sensitive uploads, the gateway is also the first line of defence against abuse (bot submissions, credential stuffing, oversized payload attacks).

4.2 Application Intake Service

Owns the applicant-facing workflow: collecting personal details, uploading a document photo and a selfie/video, and validating basic input quality (file too blurry, wrong file type, missing required field) before anything expensive happens downstream. This is the “front desk” — cheap, fast checks that reject obviously incomplete submissions early.

4.3 Encrypted Object Storage

Raw document images and selfie videos are large binary files and extremely sensitive. They are never stored in the main relational database. Instead they go into an object store (like S3-class storage) with server-side encryption, strict bucket policies, and short, monitored access windows — only specific verification services can read them, and every read is logged.

4.4 Verification Orchestrator

A workflow engine that coordinates the multi-step pipeline for each application: call document forensics, then face match, then dedup, then eligibility, then scoring — handling retries, timeouts, and partial failures for each step. Using an explicit orchestrator (rather than services calling each other directly) makes the pipeline easy to observe, easy to add new checks to, and resilient to any single check being temporarily down.

4.5 Document Forensics Service

A specialised service (often backed by a machine learning model, sometimes combined with a third-party vendor API) that inspects the uploaded document for authenticity signals: template matching against known document layouts per country, tamper detection, and text extraction (OCR) of the printed fields.

4.6 Liveness & Face-Match Service

Confirms the selfie is from a live person and computes a similarity score between the selfie and the face photo printed on the document.

4.7 Deduplication / Entity Resolution Service

Compares the extracted identity (name, date of birth, document number, and increasingly a hashed biometric template) against previously verified identities to catch the same real person trying to badge multiple accounts.

4.8 Eligibility Rules Engine

A configurable rules engine (not hardcoded logic) that encodes the platform’s specific business criteria for who deserves a badge — follower counts, account age, business registration status, professional category — separate from the identity-proofing checks, because eligibility rules change far more often than identity-proofing techniques.

4.9 Fraud & Risk Scoring Service

Combines every signal produced upstream — plus behavioural signals like device fingerprint, IP reputation, and submission velocity — into a single risk score used for routing.

4.10 Decision Router and Human Review Queue

Applies score thresholds to route each case, and for the middle band, presents a structured review interface to trained human reviewers, complete with all evidence and the reasons the automated system flagged the case.

4.11 Badge Issuance Service

The system of record for who currently holds a badge. It is intentionally the only service allowed to flip the public-facing “verified” flag on a profile, so that badge status can never be set by a bug in an upstream service.

4.12 Audit & Compliance Log

An append-only, tamper-evident log of every decision made and by what (or whom), used for regulatory audits, internal investigations, and appeals.

i
What an Interviewer May Ask

“Why separate the Eligibility Rules Engine from the Fraud & Risk Scoring Service instead of merging them into one service?” Good answer: they change at very different rates and are owned by different teams — eligibility rules are business/product decisions updated frequently (e.g., “badges now require 5,000 followers instead of 10,000”), while fraud scoring models are updated by a data science team on a slower, carefully validated release cycle. Separating them lets each evolve independently without redeploying or re-testing the other.

4.13 Ownership Boundaries and Team Structure

A useful way to check whether service boundaries are drawn correctly is to ask which team would own the pager for each box in the diagram. The Document Forensics and Face Match services are naturally owned by a machine learning platform team, since their work is mostly about model quality, training data, and inference infrastructure. The Eligibility Rules Engine is naturally owned by a product/trust-and-safety team, since it encodes business policy that changes with product strategy. The Badge Issuance Service and Audit Log are naturally owned by a core platform team, since they are the system of record and carry the highest consistency and compliance requirements. Drawing these lines clearly up front avoids the common failure mode where every team touches every service and nobody is confidently accountable for any single piece.

4.14 Communication Patterns Between Components

Not every connection in the architecture diagram is the same kind of call, and conflating them is a common design mistake. Three distinct communication patterns show up in this system:

  • Synchronous request/response — used only where the caller genuinely cannot proceed without an immediate answer, such as the Intake Service validating that an uploaded file is a supported image format before accepting the submission.
  • Asynchronous, queue-based messaging — used for the bulk of the pipeline, where the caller does not need an immediate answer and can tolerate the result arriving moments later, such as document forensics analysis.
  • Event-driven, publish/subscribe — used for fan-out notifications where multiple independent consumers care about the same fact, such as the “badge granted” event being consumed simultaneously by the Notification Service, the Audit Log, and an analytics pipeline, without the Badge Issuance Service needing to know any of those consumers exist.

4.15 Configuration and Feature Flag Service

A dedicated configuration service holds tunable parameters that operators need to change quickly without a full deployment: risk-score thresholds for each decision lane, per-country document template versions, and feature flags to disable a specific check (for example, temporarily bypassing a third-party vendor that is having an outage) while a fix is rolled out. Keeping this configuration external and dynamically reloadable, rather than hardcoded into each service’s deployment, is what lets an on-call engineer respond to an incident in minutes instead of waiting for a full build-and-deploy cycle.

05

Internal Working — How a Single Application Moves Through the System

Let’s trace one applicant, Aisha, applying for a verified badge as a small business owner, step by step through the internals.

5.1 Step 1 — Submission

Aisha fills a form: business name, category, registration number, and uploads a photo of her national ID and a short selfie video. The Application Intake Service performs synchronous, cheap validation: file size limits, image resolution minimum, blur detection, and required-field checks. If anything fails here, Aisha gets instant feedback — no need to wait for a slow pipeline to tell her the photo was too dark.

5.2 Step 2 — Secure Upload

Once basic checks pass, the raw files are uploaded directly from Aisha’s device to the encrypted object store using a short-lived, pre-signed upload URL, so the raw bytes never pass through the intake service’s own servers unnecessarily — reducing the attack surface and load on that service.

5.3 Step 3 — Enqueue for Processing

The intake service writes an “application created” event to the ingestion queue with a reference to the stored files — not the files themselves — keeping messages small and the queue fast.

5.4 Step 4 — Orchestrated Pipeline

The orchestrator picks up the event and runs the pipeline stages. Each stage is called through its own API, with a timeout and retry policy, and its result (pass/fail/score plus structured reasons) is written back to the application’s record.

VerificationOrchestrator.java — simplified orchestrator step using a workflow-style pattern; hard fails short-circuit the rest of the pipeline.
// Simplified orchestrator step, Java, using a workflow-style pattern
public class VerificationOrchestrator {

    public VerificationResult process(Application app) {
        VerificationResult result = new VerificationResult(app.getId());

        StepResult docCheck = callWithRetry(() ->
            documentForensicsClient.analyze(app.getDocumentRef()), 3);
        result.add("document_forensics", docCheck);

        if (docCheck.isHardFail()) {
            // No point continuing; document is clearly fraudulent
            result.setOutcome(Outcome.AUTO_REJECT);
            return result;
        }

        StepResult faceCheck = callWithRetry(() ->
            faceMatchClient.match(app.getSelfieRef(), app.getDocumentRef()), 3);
        result.add("face_match", faceCheck);

        StepResult dedupCheck = callWithRetry(() ->
            dedupClient.resolve(app.getExtractedIdentity()), 3);
        result.add("dedup", dedupCheck);

        StepResult eligibility = eligibilityRulesEngine.evaluate(app);
        result.add("eligibility", eligibility);

        double riskScore = riskScoringClient.score(app, result);
        result.setRiskScore(riskScore);
        result.setOutcome(decisionRouter.route(riskScore, result));

        return result;
    }
}

5.5 Step 5 — Decision Routing

The Decision Router applies thresholds. Say the risk score is 0–100, where lower is safer:

  • 0–20: auto-approve, badge issued immediately
  • 21–70: routed to the Human Review Queue with all evidence attached
  • 71–100: auto-reject, with a generic (non-revealing) rejection reason shown to the applicant
Common Mistake

Showing applicants the exact reason for an automated rejection (“your document failed hologram check at region [x,y]”) is a gift to fraudsters — it teaches them exactly what to fix on their next fake document. Rejection messages shown to users should be genuinely helpful but deliberately generic about the specific forensic signal that triggered the failure.

5.6 Step 6 — Human Review

A trained reviewer opens Aisha’s case in a review console showing: the document image, the selfie, extracted OCR fields, the dedup result, the eligibility check result, and a plain-language summary of why the automated score landed in the “review” band. The reviewer can approve, reject with a specific reason code, or request additional evidence from Aisha.

5.7 Step 7 — Badge Issuance

On approval (automated or human), the orchestrator calls the Badge Issuance Service, which is the only writer of the public “is_verified” flag. It records the decision, timestamps it, links it to the evidence bundle for audit, and publishes a “badge granted” event.

5.8 Step 8 — Notification

A Notification Service consumes the “badge granted” or “application rejected” event and sends Aisha an email/push notification, decoupled from the decision-making path so a slow email provider never blocks a verification decision.

5.9 Handling a Mid-Pipeline Failure

Suppose the Face Match Service times out while processing Aisha’s selfie because a third-party vendor is briefly overloaded. The orchestrator does not fail the whole application immediately. Instead it applies the retry policy described in 5.4 — a small number of retries with exponential backoff. If all retries are exhausted, the orchestrator marks that single stage as “inconclusive” rather than “failed,” and the Decision Router treats inconclusive stages as an automatic route into human review, never as an automatic rejection. This distinction matters: a temporary vendor outage should never translate into a genuine applicant being wrongly rejected.

5.10 Handling Duplicate Submissions

If Aisha’s spotty mobile connection causes her app to retry the submit request, the idempotency key described in 9.4 ensures the Intake Service recognises the retry and returns the existing application rather than creating a second one. This protects both Aisha (she doesn’t get confused by two pending applications) and the pipeline (no wasted duplicate processing of the same evidence).

5.11 Handling a Positive Dedup Match

Suppose the Deduplication Service finds that the face embedding extracted from Aisha’s selfie is a near-identical match to an embedding already linked to a different, previously verified account. This does not automatically mean fraud — people do occasionally have a second legitimate account, or the earlier account may have been mistakenly verified. The dedup result is treated as a strong risk signal that routes the case directly into human review with both linked application records shown side by side, so a trained reviewer — not an automated rule — makes the final call on what looks like a genuinely ambiguous case.

5.12 Step 9 — Ongoing Monitoring After Approval

Approval is not the end of Aisha’s journey through the system. Her badge record is tagged with the risk score and evidence bundle used to approve her, and enters the standard re-verification schedule described in 6.2. If her account later shows a sudden change in payout details, business name, or login location, a behavioural trigger can open a new, lightweight re-verification case automatically, without waiting for the scheduled renewal date.

06

Data Flow & Lifecycle

Understanding how data moves — and how long it lives — is as important as understanding the processing steps, because this system’s biggest risk is not downtime, it is a data breach or unlawful retention.

6.1 Application Lifecycle States

StateMeaningWho can trigger transition
DRAFTApplicant started but has not submittedApplicant
SUBMITTEDAll required fields/files present, queued for pipelineIntake Service
IN_REVIEW_AUTOMATEDPipeline actively scoring the applicationOrchestrator
IN_REVIEW_HUMANWaiting on a human reviewer decisionDecision Router
APPROVEDBadge grantedBadge Issuance Service
REJECTEDBadge deniedDecision Router or Reviewer
APPEALEDApplicant contested a rejectionAppeals Service
EXPIREDBadge revoked after periodic re-verification failedRe-verification Job

6.2 Re-verification and Badge Expiry

A verified badge should not be a permanent, unmonitored stamp. People change roles, sell accounts, or lose control of a business. Production systems typically schedule periodic re-verification (for example, every 12–24 months) or trigger event-based re-verification when suspicious signals appear, like a sudden change of email, phone number, and payout account on the same day.

i
What an Interviewer May Ask

“How would you design badge expiry so it doesn’t create a huge synchronised load spike once a year?” Strong answer: never expire badges on a single fixed calendar date; instead, set each badge’s expiry to (issue_date + N months) so renewals are naturally spread across the calendar, and additionally jitter reminder-notification send times to avoid a thundering herd on the notification and review systems.

6.3 Enforcing the State Machine

The lifecycle states in the table above are not just documentation — they are enforced in code as an explicit state machine, where each state defines exactly which transitions are legal from it. This prevents an entire category of bugs where a race condition or a bug in one service could otherwise push an application into an invalid combination, such as moving directly from DRAFT to APPROVED without ever passing through the pipeline.

ApplicationState.java — a static ALLOWED map that rejects illegal transitions at the application layer before they can reach the database.
public enum ApplicationState {
    DRAFT, SUBMITTED, IN_REVIEW_AUTOMATED, IN_REVIEW_HUMAN,
    APPROVED, REJECTED, APPEALED, EXPIRED;

    private static final Map<ApplicationState, Set<ApplicationState>> ALLOWED = Map.of(
        DRAFT, Set.of(SUBMITTED),
        SUBMITTED, Set.of(IN_REVIEW_AUTOMATED),
        IN_REVIEW_AUTOMATED, Set.of(IN_REVIEW_HUMAN, APPROVED, REJECTED),
        IN_REVIEW_HUMAN, Set.of(APPROVED, REJECTED),
        REJECTED, Set.of(APPEALED),
        APPEALED, Set.of(IN_REVIEW_HUMAN),
        APPROVED, Set.of(EXPIRED)
    );

    public boolean canTransitionTo(ApplicationState next) {
        return ALLOWED.getOrDefault(this, Set.of()).contains(next);
    }
}

Any service attempting to write a state transition that this map does not allow fails loudly at the application layer, well before it could ever reach the database — turning what would otherwise be a subtle, hard-to-trace data integrity bug into an immediate, visible error during development and testing.

6.4 Data Minimisation Across the Lifecycle

Different pieces of an application’s data reach the end of their usefulness at different points in the lifecycle, and a mature system deletes each piece as soon as it stops being needed rather than waiting for one single blanket cleanup job. The raw document and selfie files are needed only until a decision is final and any appeal window has closed; the extracted OCR text fields may be needed slightly longer, to support dedup checks against future applications; the final decision, risk score, and audit trail are needed for the long term for compliance purposes, but by that point they no longer need to reference the raw underlying files at all.

07

Data Storage Design

This system needs several very different storage engines, because the data itself has very different shapes and access patterns.

7.1 Relational Database — Application & Decision Records

The core “application” and “decision” records are structured, relational, and need strong consistency (you cannot have an application that is simultaneously APPROVED and REJECTED). A relational database (PostgreSQL-class) with clear foreign keys fits well here.

applications.sql — raw document bytes are never stored inline; only a reference pointer to the object store lives here.
CREATE TABLE applications (
    id              UUID PRIMARY KEY,
    user_id         UUID NOT NULL,
    applicant_type  VARCHAR(20) NOT NULL, -- INDIVIDUAL / BUSINESS
    state           VARCHAR(30) NOT NULL,
    risk_score      NUMERIC(5,2),
    document_ref    VARCHAR(200) NOT NULL, -- pointer into object store, not raw data
    selfie_ref      VARCHAR(200) NOT NULL,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    decided_at      TIMESTAMPTZ,
    reviewer_id     UUID,
    CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE INDEX idx_applications_state ON applications(state);
CREATE INDEX idx_applications_user  ON applications(user_id);

7.2 Object Storage — Raw Documents & Selfies

As described earlier, raw evidence files live in an encrypted object store, never in the relational database (which would bloat backups, slow queries, and widen the blast radius of any DB-level breach).

7.3 Vector/Hash Store — Biometric Templates for Dedup

To detect the same real person applying under multiple identities, the Deduplication Service does not store raw face photos for comparison; it stores a mathematical face embedding (a fixed-length vector representing facial features) and searches for nearest neighbours using a vector index. This lets the system answer “has a face very similar to this one been verified before?” without keeping searchable copies of everyone’s photo.

Common Mistake

Storing raw biometric images “just in case, for future dedup improvements” is both a security liability and, in many jurisdictions (e.g., under GDPR-style biometric data rules), potentially unlawful without very specific consent and retention justification. Store the minimum derived representation needed, not the raw source.

7.3.1 Approximate Nearest Neighbour Search

Comparing a new face embedding against millions of stored embeddings one at a time would be far too slow to run on every single application. Instead, the vector index uses an approximate nearest neighbour algorithm (such as HNSW — Hierarchical Navigable Small World graphs) that can find the closest matching embeddings in roughly logarithmic time rather than scanning the entire dataset linearly. The “approximate” part is a deliberate trade-off: the index might occasionally miss the single mathematically closest match in exchange for being fast enough to run at scale, which is an acceptable trade-off because the dedup service is looking for strong matches above a similarity threshold, not the single closest point in the entire dataset.

7.3.2 Sharding the Vector Index by Region

Given the regional data residency requirements discussed later in 15.2, the vector index itself is typically sharded per region rather than maintained as one global index. This is a deliberate trade-off: a fraudster could theoretically create duplicate identities across two different regions without being caught by regional dedup alone, so a slower, carefully governed cross-region matching process (with extra legal review, since it involves moving biometric derivatives across a jurisdictional boundary) handles the rarer cross-border case separately from the fast, region-local path that handles the overwhelming majority of dedup checks.

7.4 Append-Only Audit Store

Every decision, every human reviewer action, and every automated score is written to an append-only, cryptographically chained log (each entry includes a hash of the previous entry), so that tampering with historical decisions — even by an insider with database access — is detectable.

7.5 Search Index — Reviewer Queue & Investigations

Human reviewers and fraud investigators need to search and filter across thousands of pending cases (by risk score range, country, document type, flagged reason). A search index (Elasticsearch-class) mirrors a subset of application metadata for fast, flexible querying, kept eventually consistent with the source-of-truth relational database via change-data-capture.

7.6 Handling Concurrent Writes Safely

Two different actors can sometimes try to update the same application record at nearly the same moment — for example, a human reviewer clicking “approve” at the exact instant an automated re-check job decides to flag the same case for fraud. Optimistic concurrency control, using a version number column that must match on every update, prevents a lost-update bug where one write silently overwrites the other without either actor knowing:

optimistic-update.sql — zero affected rows tells the caller someone else changed the record first; re-read and retry instead of blind overwrite.
UPDATE applications
SET state = :newState, version = version + 1, decided_at = now()
WHERE id = :appId AND version = :expectedVersion;

-- If zero rows are affected, the caller knows someone else changed
-- this record first, and can re-read the latest state before retrying
-- instead of blindly overwriting a concurrent decision.

7.7 Archival Strategy

Once an application has been in a terminal state (APPROVED, REJECTED, EXPIRED) for a defined period, its full record — minus the already-deleted raw evidence — is moved from the hot primary database into a cheaper, append-oriented archival store (a columnar warehouse table, for example), keeping the primary database’s working set small and its queries fast, while still preserving historical records for audits, legal holds, and long-term trend analysis.

08

Caching, Queues & Asynchronous Processing

8.1 Why Queues Are the Backbone Here

Identity verification checks — document forensics, face matching, external registry lookups — can each take anywhere from a few hundred milliseconds to several seconds, and third-party vendor APIs occasionally have outages or slowdowns. If the applicant’s browser waited synchronously for the full pipeline, timeouts and a poor user experience would be constant. Instead, submission is fast and synchronous (just intake validation), while the actual verification pipeline runs asynchronously off a message queue.

8.2 Fan-Out / Fan-In Pattern

Independent checks (document forensics, liveness, dedup) do not depend on each other’s output, so they can run in parallel (fan-out) rather than sequentially, cutting total pipeline latency roughly to the slowest single stage instead of the sum of all stages. The orchestrator then waits for all required results before computing the final score (fan-in).

8.3 Caching Layer

Caching plays a smaller role here than in a typical read-heavy consumer app, since most data is write-once and sensitive, but it still matters in specific places:

  • Eligibility rule configuration — cached in memory across rules-engine instances, since rules change rarely but are read on every single application.
  • Reviewer queue counts / dashboard stats — cached with a short TTL (a few seconds) so dashboards stay fast without hammering the database with aggregate queries.
  • Country/document-template metadata — cached, since the set of “what does a valid Indian passport template look like” changes only a few times a year.
Common Mistake

Never cache anything derived from an individual applicant’s raw PII or biometric data in a shared cache layer like Redis unless it is encrypted and access-scoped exactly like the primary data store — a cache is still a copy of sensitive data and needs the same protection.

8.4 Dead Letter Queues and Retry Policy

If a downstream check (e.g., a third-party document forensics vendor) is temporarily down, messages should retry with exponential backoff, and after a bounded number of attempts move to a dead-letter queue for manual or automated later reprocessing — so a vendor outage never silently drops applications.

09

APIs & Microservices

9.1 Service Boundaries

Each microservice owns one clear responsibility and its own data:

ServiceOwnsExposes
Intake ServiceApplications in DRAFT/SUBMITTED state, basic validationREST: submit application, upload URLs
Document Forensics ServiceDocument authenticity modelsgRPC: analyze(document_ref)
Face Match ServiceLiveness + face similarity modelsgRPC: match(selfie_ref, doc_ref)
Dedup ServiceFace embedding vector index, identity hash indexgRPC: resolve(identity)
Eligibility EngineBusiness rules configurationREST: evaluate(application)
Risk Scoring ServiceFraud/risk ML modelgRPC: score(application, evidence)
Badge Issuance ServiceSource of truth for badge statusREST: issue/revoke badge
Appeals ServiceAppeal records and re-review triggersREST: submit appeal

9.2 Why gRPC for Internal Calls

Internal calls between the orchestrator and the ML-backed services (forensics, face match, scoring) are high-frequency, latency-sensitive, and strongly typed (well-defined score/confidence fields), which is exactly where gRPC’s binary protocol and generated strongly-typed clients outperform plain REST/JSON. Public-facing and less latency-critical services (Intake, Appeals) use REST, which is easier for client apps and third-party integrations to consume.

9.3 Sample External API

POST /v1/verification/applications — the applicant-facing submission call returns pre-signed upload URLs so raw bytes never touch the service.
POST /v1/verification/applications
Authorization: Bearer <user_token>
Content-Type: application/json

{
  "applicant_type": "BUSINESS",
  "business_name": "Aisha Textiles Pvt Ltd",
  "business_registration_number": "U17124DL2019PTC123456",
  "country": "IN"
}

Response 201:
{
  "application_id": "3f2a9c...",
  "state": "DRAFT",
  "upload_urls": {
    "document": "https://uploads.example/pre-signed/doc/3f2a9c",
    "selfie": "https://uploads.example/pre-signed/selfie/3f2a9c"
  }
}

9.4 Idempotency Keys

Every submission-triggering endpoint accepts a client-generated idempotency key, so a retried request (from a flaky mobile network) does not create a duplicate application.

SubmitController.java — retry-safe submit: same idempotency key returns the existing application record instead of creating a new one.
public ResponseEntity<Application> submit(
        @RequestHeader("Idempotency-Key") String idemKey,
        @RequestBody SubmitRequest req) {

    Optional<Application> existing = applicationRepo.findByIdemKey(idemKey);
    if (existing.isPresent()) {
        return ResponseEntity.ok(existing.get()); // safe to return same result again
    }
    Application app = applicationService.createAndEnqueue(req, idemKey);
    return ResponseEntity.status(201).body(app);
}

9.5 API Gateway Responsibilities

  • Authentication (verifying the user’s session/token)
  • Per-user rate limiting (e.g., max 3 verification submissions per day)
  • Request size limits (rejecting absurdly large file uploads before they reach any backend service)
  • Basic bot detection (challenge suspicious traffic before it reaches the Intake Service)

9.6 The Appeals API

POST /v1/verification/applications/{id}/appeals — an appeal is its own resource, layered on top of the original decision rather than mutating it.
POST /v1/verification/applications/{applicationId}/appeals
Authorization: Bearer <user_token>

{
  "reason": "I believe my document was misread; uploading a clearer photo.",
  "supplemental_document_ref": "upload/appeal-doc-88213"
}

Response 202:
{
  "appeal_id": "a91f...",
  "state": "PENDING_REVIEW",
  "original_application_id": "3f2a9c...",
  "estimated_review_window_hours": 72
}

Notice the appeal is modelled as its own resource, linked to but distinct from the original application. This keeps the audit trail clean — an appeal is a new event with its own timestamp and reviewer, layered on top of the original decision rather than mutating it, which matters for the tamper-evident audit log described in 7.4.

9.7 Consistent Error Handling

Internal service-to-service calls use a standard error taxonomy so the orchestrator can make consistent routing decisions regardless of which downstream service failed:

StageErrorType.java — distinguishing transient, terminal, unavailable, and inconclusive errors so the orchestrator can route each case correctly.
public enum StageErrorType {
    VALIDATION_ERROR,      // bad input, will never succeed on retry - do not retry
    TRANSIENT_ERROR,       // temporary issue, safe to retry with backoff
    DOWNSTREAM_UNAVAILABLE,// circuit breaker should engage
    INCONCLUSIVE           // check ran but could not reach a confident result
}

public class StageResult {
    private final boolean success;
    private final StageErrorType errorType; // null if success
    private final double confidence;
    private final Map<String, Object> signals;

    public boolean isRetryable() {
        return errorType == StageErrorType.TRANSIENT_ERROR;
    }
}

Distinguishing these categories explicitly, rather than treating every failure the same way, is what allows the orchestrator to apply the right response automatically: retry a transient blip, engage the circuit breaker for a genuinely unavailable dependency, and route an inconclusive result to human review as described in 5.9, all without a human having to manually classify every single failure after the fact.

10

Design Patterns & Anti-Patterns

10.1 Patterns Worth Using

PatternWhere it applies here
Saga (choreography or orchestration)Coordinating the multi-step verification pipeline with compensating actions if a later step invalidates an earlier one
Strategy PatternSwapping document-forensics logic per country/document type behind a common interface
Circuit BreakerProtecting the orchestrator from a slow/failing third-party KYC vendor
CQRSSeparating the write-heavy application pipeline from the read-heavy reviewer dashboard/search index
Outbox PatternReliably publishing “badge granted” events at the same time as the DB write, without dual-write inconsistency
Rules Engine PatternExternalising eligibility logic so product teams can change thresholds without code deploys

10.2 Anti-Patterns to Avoid

avoid

God Service

Putting document parsing, face matching, dedup, eligibility, and badge issuance all inside one giant “VerificationService” class or deployable. It becomes impossible to scale, test, or update any one part without risking every other part, and a bug in an experimental new fraud rule can take down badge issuance entirely.

avoid

Synchronous Waterfall

Calling forensics, then waiting, then calling face match, then waiting, then calling dedup, all in a single blocking request-response chain. Total latency becomes the sum of every stage, and one slow vendor call blocks the entire pipeline for every applicant behind it.

avoid

Silent Auto-Approve Drift

Letting a risk-scoring model’s threshold silently determine more and more auto-approvals over time without a human audit sample. Systematic model blind spots (a document-forgery technique the model has never seen) can go undetected for a long time.

10.3 Consistency Model Across the Pipeline

Because the verification pipeline spans multiple independently-scaled services communicating asynchronously, the system as a whole is eventually consistent rather than strongly consistent — there can be a brief window where an application’s individual stage results exist but the overall decision has not yet been computed. This is an acceptable and deliberate trade-off everywhere except the single moment of badge issuance itself, where the Badge Issuance Service enforces strong consistency (7.6’s optimistic concurrency control) because a badge is a highly visible, public-facing fact that must never flicker between states or be granted twice through a race condition.

10.4 The Saga Pattern in Practice

Because the pipeline touches several independently owned services, a failure partway through needs a defined compensating action rather than leaving the system in an inconsistent half-finished state. For example, if the Badge Issuance Service successfully grants a badge but the subsequent audit-log write fails, a compensating step must either retry the audit write until it succeeds (preferred, since the badge grant is already publicly visible and should not be silently reversed for an unrelated logging failure) or, for less consequential steps, roll back cleanly. Designing each pipeline stage with an explicit compensating action in mind — “if this succeeds but a later stage fails, what do we do?” — is the core discipline of the saga pattern, and skipping this exercise is how systems end up with quietly inconsistent state that only surfaces months later during an audit.

10.5 Strategy Pattern for Country-Specific Document Rules

DocumentValidationStrategy.java — each country’s forensic logic lives behind a common interface, so adding a new country means adding an implementation, not editing a shared conditional block.
public interface DocumentValidationStrategy {
    ValidationResult validate(DocumentImage image, DocumentType type);
}

public class IndianDocumentStrategy implements DocumentValidationStrategy {
    public ValidationResult validate(DocumentImage image, DocumentType type) {
        // Checks specific to Indian Aadhaar / PAN / Passport templates
        return templateMatcher.match(image, IndiaTemplates.forType(type));
    }
}

public class DocumentStrategyFactory {
    private final Map<String, DocumentValidationStrategy> strategies;

    public DocumentValidationStrategy forCountry(String countryCode) {
        return strategies.getOrDefault(countryCode, strategies.get("DEFAULT"));
    }
}

This pattern keeps country-specific forensic logic isolated behind a common interface, so adding support for a new country’s document formats means adding a new strategy implementation, not modifying a large shared conditional block that every other country’s logic also depends on.

11

Performance & Scalability

11.1 Scaling Dimensions

This system needs to scale along two different axes that behave very differently:

  • Submission throughput — bursty, driven by product launches and marketing pushes. Handled by horizontally scaling the stateless Intake Service and API Gateway behind a load balancer, and by the ingestion queue absorbing bursts so downstream workers can catch up at a steady pace.
  • Pipeline processing throughput — steadier, driven by queue depth. Handled by auto-scaling worker pools (forensics, face match, dedup) based on queue length rather than CPU alone, since these are I/O- and model-inference-bound.

11.2 Horizontal Scaling of Stateless Services

The Intake Service, API Gateway, and orchestrator instances hold no session state locally (state lives in the database and queue), so they can be scaled horizontally by simply adding more instances behind a load balancer during a traffic spike.

11.3 Batching for ML Inference

Document forensics and face-match models run more efficiently when given a batch of images at once rather than one at a time, since GPU/accelerator utilisation improves with batch size. Workers can accumulate a small batch (e.g., up to 16 images or 50 milliseconds, whichever comes first) before running inference, trading a small, bounded latency increase for a large throughput gain.

11.4 Database Scaling

The applications table grows continuously and is read/written heavily by both the pipeline and the reviewer dashboard. Common techniques: read replicas for the reviewer dashboard’s read-heavy queries, partitioning the applications table by creation month for easier archival of old, decided applications, and moving the read-heavy “reviewer queue” workload onto the search index (per Chapter 8) rather than hitting the primary database directly.

11.5 Load Balancing

A Layer 7 load balancer distributes incoming API traffic across Intake Service instances using least-connections or round-robin, with health checks removing unhealthy instances automatically. Internally, gRPC calls between the orchestrator and ML services use client-side load balancing with service discovery, avoiding a single internal load balancer becoming a bottleneck at high internal call volume.

i
What an Interviewer May Ask

“Marketing announces badges will open to everyone tomorrow at 9am, and you expect 50x normal submission volume for a few hours. What do you actually do, concretely?” Good answer: pre-scale the Intake Service and gateway ahead of the announcement (don’t rely purely on reactive auto-scaling, which has a ramp-up lag), make sure the ingestion queue has enough retention/capacity to buffer a large backlog, verify third-party vendor rate limits and negotiate a temporary higher quota if needed, and communicate to applicants that “processing may take longer than usual” so a growing queue doesn’t look like a broken system.

11.6 Capacity Planning with Little’s Law

A simple but genuinely useful way to reason about how many worker instances a pipeline stage needs is Little’s Law: the average number of items in a system equals the arrival rate multiplied by the average time each item spends in the system. If applications arrive at 500 per second at peak, and the face-match stage takes an average of 400 milliseconds per application, then at any given instant roughly 200 applications are “in flight” at that stage — which tells the capacity planning team roughly how many concurrent worker slots that stage needs, before even worrying about safety margins for spikes above the expected peak.

11.7 Cold Start and Warm Pools for ML Inference

Model-serving containers for the forensics and face-match services can take several seconds to load large model weights into memory, which is far too slow to do on-demand when a burst of traffic arrives. Production deployments keep a warm pool of pre-loaded model-serving instances sized for typical peak load, with additional instances pre-warmed ahead of predictable spikes (like the marketing-announcement scenario above) rather than relying purely on reactive auto-scaling that would leave early requests waiting on a cold-starting container.

11.8 Read/Write Split for the Reviewer Dashboard

The reviewer dashboard is read-heavy and latency-sensitive from a human’s perspective (reviewers get frustrated by a slow-loading queue), while the pipeline itself is write-heavy. Directing dashboard reads to database read replicas, or better, to the dedicated search index described in 7.5, keeps heavy analytical and browsing queries from competing with the primary database’s write path for the pipeline itself, which is a much higher-priority workload from the business’s perspective.

12

High Availability & Reliability

12.1 Redundancy

Every stateless service runs multiple instances across at least two availability zones, so the failure of one machine, rack, or zone does not take the system down. The primary database runs with synchronous replication to a standby in a second zone, with automated failover.

12.2 Graceful Degradation

If a non-critical dependency fails (say, the country/document-template metadata cache), the system should degrade gracefully — falling back to a slower direct lookup — rather than failing the entire application. If a critical dependency fails (say, the face-match service is fully down), new applications should queue safely rather than being lost, and applicants should see “your application is being processed, this may take longer than usual” instead of a hard error.

12.3 Circuit Breakers for Third-Party Vendors

ForensicsClient.java — wrapping vendor calls with a circuit breaker: an OPEN breaker fails fast instead of piling up waiting threads on a struggling vendor.
public class ForensicsClient {
    private final CircuitBreaker breaker = CircuitBreaker.ofDefaults("forensicsVendor");

    public StepResult analyze(String documentRef) {
        return breaker.executeSupplier(() -> {
            try {
                return vendorApi.call(documentRef);
            } catch (VendorTimeoutException e) {
                throw new DownstreamUnavailableException(e);
            }
        });
        // When breaker is OPEN, calls fail fast instead of piling up
        // waiting threads on a vendor that is already struggling.
    }
}

12.4 Disaster Recovery

Given the extreme sensitivity of the data, disaster recovery planning covers both technical failure (region outage, recovering from encrypted backups with a defined Recovery Point Objective and Recovery Time Objective) and process failure (a compromised signing key for badge issuance, requiring an emergency key-rotation runbook that can freeze new badge issuance instantly while investigation happens).

12.5 Backpressure

Rather than letting an overloaded downstream worker pool silently accumulate unbounded latency, the ingestion queue exposes depth metrics, and the Intake Service can apply backpressure — briefly slowing accepted submission rate or showing applicants an estimated wait time — instead of accepting unlimited work the pipeline cannot keep up with.

12.6 Defining Realistic SLAs

A service level agreement for this kind of system needs to be honest about the difference between the automated and human-review lanes, since promising one blanket turnaround time across both leads to either wildly padded expectations or broken promises. A workable structure sets separate targets: automated decisions (auto-approve or auto-reject) within a small number of minutes at the 95th percentile, and human-reviewed decisions within a defined number of business hours or days depending on current queue depth, with the applicant-facing product communicating whichever lane their specific application has landed in rather than a single generic estimate.

12.7 Health Checks and Readiness Probes

Each service exposes both a liveness check (is the process still running and able to respond at all?) and a readiness check (is the process able to handle real traffic right now — for example, has it finished loading its model weights, and can it reach its downstream dependencies?), so the orchestration platform can correctly avoid routing traffic to an instance that is technically alive but not yet actually ready, a distinction that matters a great deal for the ML-backed services where model loading can take longer than a typical container startup.

12.8 Chaos Testing

Because this system’s failure modes are high-stakes (a silent failure could mean lost applications or wrongly issued badges, not just a slow page load), production-grade deployments periodically run controlled chaos experiments — deliberately killing a service instance, injecting artificial latency into a vendor call, or simulating a queue backlog — in a staging environment, and eventually in production with careful guardrails, to verify that the retry policies, circuit breakers, and backpressure mechanisms described above actually behave as designed rather than only in theory.

12.9 Runbooks for On-Call Engineers

Given the compliance sensitivity of this system, on-call response cannot simply be “restart the service and see if it helps,” which is an acceptable first response for many ordinary web applications but is risky here, since a hasty restart mid-pipeline could leave an application in an ambiguous state. Each critical failure scenario — vendor outage, queue backlog beyond a defined threshold, a spike in auto-reject rate — has a documented runbook specifying the safe diagnostic steps, who to escalate to, and explicitly what not to do (for example, never manually flip an application’s state directly in the database, always go through the orchestrator’s compensating actions) to avoid an on-call engineer accidentally creating an inconsistent or unauditable state while trying to fix an incident quickly.

13

Security

Security is not a section bolted onto this system — it is close to the entire point of it. A verified badge system that leaks the data it was verifying, or that can be tricked into badging fraudulent accounts, is worse than not having one at all.

13.1 Encryption

  • In transit: TLS everywhere, including internal service-to-service calls, not just the public-facing edge.
  • At rest: All document images, selfies, and biometric embeddings encrypted with keys managed by a dedicated key management service, with envelope encryption so raw data-encryption keys are never stored alongside the data they protect.

13.2 Access Control

Strict role-based access control: an application engineer debugging a queue backlog should never be able to view raw document images; only the specific automated services and a narrow, logged set of trained human reviewers should have that access, each request individually audited.

Common Mistake

Granting broad “read all tables” database access to internal analytics or BI tools that happen to sit on the same database instance as raw PII tables. Sensitive PII tables should live in a separately access-controlled schema or database, with analytics working only from anonymised or aggregated views.

13.3 Defending Against Deepfakes and Synthetic Media

As generative AI makes convincing fake selfies and even fake “talking head” liveness videos cheaper to produce, the Liveness & Face-Match Service needs defences beyond a single static check: randomised, unpredictable liveness challenges (so a pre-recorded deepfake video can’t anticipate the exact prompt), texture/frequency analysis that can reveal GAN-generated image artefacts invisible to the human eye, and continuous retraining as new synthetic-media techniques emerge.

13.4 Anti-Fraud: Velocity and Network Analysis

Beyond checking one application in isolation, the Fraud & Risk Scoring Service looks at patterns across many applications: many submissions from the same device fingerprint or IP range in a short window, documents that are visually near-duplicates of previously rejected fakes, and payout/bank details shared across supposedly unrelated “verified business” accounts.

13.5 Authentication & Authorisation for Reviewers

Human reviewers authenticate with hardware-backed multi-factor authentication given the sensitivity of what they can see and decide, and their review console enforces least-privilege: a junior reviewer might handle standard cases, while only senior reviewers can override an auto-reject or handle cases flagged as high-fraud-risk.

13.6 Data Retention and the Right to Erasure

Raw documents and selfies are deleted automatically after a defined retention window once a decision is final (commonly 30–90 days, tuned to legal requirements), keeping only the minimal metadata needed for audit and appeals. Applicants who withdraw an application or whose account is deleted trigger an erasure workflow that removes their raw evidence ahead of the normal schedule, consistent with data protection law such as GDPR’s right to erasure or India’s DPDP Act.

13.7 Preventing Enumeration and Scraping

Endpoints that check “is user X verified?” are rate-limited and do not reveal detailed application status to anyone other than the applicant themselves, preventing an attacker from mapping out who has and hasn’t applied, or probing for information useful in a social-engineering attack.

i
What an Interviewer May Ask

“How would you detect an insider — a reviewer — approving badges for bribes?” Strong answer: statistical monitoring of each reviewer’s approval rate versus their peers and versus the automated system’s own risk score for the same cases (an outlier reviewer who frequently overrides a high-risk score to “approve” is a red flag), mandatory second-reviewer sign-off above a certain business-impact threshold, and the tamper-evident audit log making it possible to reconstruct exactly what any reviewer saw and decided.

13.8 Threat Modelling the Pipeline

A structured threat model walks through each component asking “who might attack this, and how?” For the Intake Service, the main threats are bot-driven mass submission and oversized payload abuse — mitigated by rate limiting, CAPTCHA-style challenges for suspicious traffic, and strict upload size caps. For the Document Forensics Service, the threat is adversarially crafted fake documents designed to fool the specific model in production — mitigated by continuously refreshing training data with newly discovered forgery techniques and never publicly documenting the exact forensic checks performed. For the Badge Issuance Service, the threat is a compromised internal credential being used to directly flip a badge flag, bypassing the entire pipeline — mitigated by making the service reachable only through the orchestrator’s authenticated internal calls, never through a generic admin database write.

13.9 Secrets Management

Credentials for third-party vendor APIs, database connection strings, and encryption key references are never stored in application code or configuration files checked into source control. They are held in a dedicated secrets manager with fine-grained access policies and automatic rotation, so that a leaked configuration file or a compromised build pipeline does not directly expose long-lived credentials to sensitive systems.

13.10 Supply Chain Security for ML Models

Where third-party or open-source models are used as a starting point for the document forensics or face-match models, the origin and integrity of those model weights matter — a poisoned or backdoored pretrained model could be tuned to pass specific fraudulent documents deliberately. Production teams verify model provenance, run adversarial evaluation before deployment, and treat model artefacts with the same supply-chain scrutiny normally reserved for software dependencies.

13.11 Segregation of Duties

No single engineer or reviewer should hold enough unilateral access to both approve a fraudulent badge and cover their tracks in the audit log. Segregation of duties spreads that capability across roles — engineers who can deploy code cannot approve applications, reviewers who can approve applications cannot modify audit log infrastructure — so that abusing the system requires collusion across multiple people rather than a single compromised account.

14

Monitoring, Logging & Metrics

14.1 What to Measure

CategoryExample Metrics
ThroughputApplications submitted/min, applications processed/min per pipeline stage
Latencyp50/p95/p99 time-to-decision, per stage and end-to-end
Queue HealthIngestion queue depth, age of oldest unprocessed message, dead-letter queue size
QualityAuto-approve rate, auto-reject rate, human-review rate, appeal overturn rate
FraudDetected duplicate-identity rate, confirmed-fraud rate post-audit
ReliabilityVendor API error rate, circuit breaker open/close events, DB replication lag

14.2 Structured Logging

Every pipeline stage emits structured logs (not free-text) tagged with a shared trace ID for the application, so a single query can reconstruct the full journey of one applicant’s request across every microservice it touched — essential both for debugging and for compliance investigations.

14.3 Distributed Tracing

Because one application fans out across several services (forensics, face match, dedup) and back, distributed tracing (e.g., OpenTelemetry-style spans) is what makes it possible to see, at a glance, which stage is the bottleneck for a specific slow application, rather than guessing from disconnected logs.

14.4 Alerting

Alerts are tuned around business-impact thresholds, not just infrastructure thresholds: for example, “auto-reject rate jumped from 8% to 35% in the last hour” is at least as important an alert as “CPU usage is at 90%,” because a sudden change in decision distribution often signals a broken model, a bad rule deployment, or an active fraud attack — not just an infrastructure problem.

14.5 The Appeal Overturn Rate as a Quality Signal

A rising appeal-overturn rate (rejections that get reversed on appeal) is one of the most valuable quality metrics in the whole system, because it directly measures how often the automated or first-pass human decision was wrong — feeding back into model retraining and reviewer training.

14.6 Dashboards for Different Audiences

Different stakeholders need different views into the same underlying metrics. Engineers on call need an infrastructure dashboard focused on latency, error rates, and queue depth so they can quickly diagnose a technical incident. The trust-and-safety team needs a decision-quality dashboard focused on approval/rejection rates broken down by country and document type, so they can spot fairness issues or emerging fraud patterns. Executives need a much simpler rollup — total applications processed, average time-to-decision, and badge count — for periodic business reporting. Building one dashboard that tries to serve all three audiences usually ends up serving none of them well.

14.7 Synthetic Monitoring

Beyond watching real traffic, the system periodically runs synthetic test applications — using pre-approved, deliberately fake but harmless test documents — through the full pipeline end to end, verifying that every stage responds correctly and within expected latency, catching a silent pipeline failure (for example, a misconfigured vendor API key) before it affects real applicants rather than after.

15

Deployment & Cloud Architecture

15.1 Containerised Microservices

Each service is packaged as a container and deployed on an orchestration platform (Kubernetes-class), with each ML-backed service (forensics, face match) potentially on GPU-enabled node pools separate from the CPU-only node pools running lightweight services like the Intake Service.

15.2 Multi-Region Considerations

Given data-residency regulations, applications from users in a given region are often required, by law, to have their identity data stored and processed within that region. This typically means the system is deployed with regional data planes (e.g., one full stack in the EU, one in India, one in the US), rather than one global stack — a very different scaling story than a typical stateless consumer app.

💡
Production Example

Large global platforms that operate identity verification typically maintain region-specific processing to comply with data localisation rules — for example, ensuring biometric data collected from users in a given country is processed and stored on servers physically located within that country’s jurisdiction, rather than sent to a shared global data centre.

15.3 CI/CD and Model Deployment

Application code deploys through a standard CI/CD pipeline with automated tests. ML models (forensics, face match, risk scoring) go through a separate, more conservative pipeline: offline evaluation against a held-out labelled dataset, a shadow-mode period where the new model scores live traffic without affecting decisions, then a gradual canary rollout with close monitoring of the decision-distribution metrics described in Chapter 14.

15.4 Infrastructure as Code

Given the compliance sensitivity, every piece of infrastructure — networking rules, encryption key policies, access control lists — is defined as code and version-controlled, so any change to who can access sensitive systems is reviewable and auditable, not a manual console click that leaves no trace.

15.5 Network Segmentation

The services that handle raw document images and selfies (Intake Service, the object store, the forensics and face-match services) sit inside a more tightly restricted network segment than services that only ever touch metadata (the reviewer dashboard’s search index, the notification service). Traffic between segments passes through explicitly defined and monitored gateways rather than a flat network where any internal service could, in principle, reach any other, limiting how far an attacker who compromises one lower-sensitivity service could move laterally toward the most sensitive data.

15.6 Blue-Green and Canary Deployments

Because a bad deployment to the Badge Issuance Service or the Decision Router could have an outsized real-world impact — wrongly granting or revoking badges at scale — these particular services are deployed using a canary strategy, routing a small percentage of real traffic to the new version first and comparing its decision distribution against the stable version before a full rollout, rather than a simple all-at-once blue-green swap that offers less opportunity to catch a subtle behavioural regression before it affects every applicant.

15.7 Cost Considerations

ML inference for document forensics and face matching is typically the largest infrastructure cost driver in this system, since it runs on specialised accelerated hardware. Techniques from Chapter 11 — batching, warm pools sized to typical rather than absolute peak load with burst capacity layered on top, and caching stable metadata like document templates — directly translate into meaningful cost savings at the scale of millions of applications, making performance engineering here a cost concern as much as a latency concern.

16

Advantages, Disadvantages & Trade-offs

Advantages

  • Reduces impersonation and fraud, building platform trust.
  • Automation lets the system absorb huge volume spikes cheaply.
  • Human-in-the-loop review catches automated-model blind spots.
  • Modular pipeline lets individual checks improve independently.

Disadvantages

  • Extremely high compliance and security burden.
  • False rejects can alienate legitimate users and create PR risk.
  • Third-party vendor dependency for forensics/face-match introduces external risk.
  • Human review does not scale linearly — it is the most expensive lane.

16.1 Key Trade-off: Automation vs Human Judgment

Pushing more cases into full automation lowers cost and latency but increases the risk of systematic, hard-to-notice errors (a model with a blind spot for one country’s document format might silently reject thousands of legitimate applicants from that country). Pushing more cases to human review improves accuracy and explainability but is far more expensive and slower, and doesn’t scale as cleanly with sudden volume spikes. Most production systems tune the auto-approve/auto-reject thresholds conservatively at first and widen them only as confidence in the model grows, verified by continuous audit sampling.

16.2 Key Trade-off: Strictness vs Accessibility

Requiring strong document proof (passport, national ID) is more fraud-resistant but excludes people who lack easy access to such documents — a real accessibility and equity concern, especially in regions with lower rates of formal identity documentation. Systems often need alternate verification paths (e.g., video interview, trusted third-party attestation) for these cases, at the cost of additional engineering and review complexity.

16.3 Key Trade-off: Build vs Buy for Identity Proofing

Building document forensics and face-matching models in-house gives full control over accuracy tuning, cost structure at scale, and independence from a vendor’s pricing or availability, but requires sustained investment in specialised machine learning talent and continuously refreshed training data to keep pace with evolving forgery techniques. Buying this capability from a specialised vendor gets a team to market faster and benefits from a vendor’s cross-customer fraud pattern visibility, but introduces an external dependency, ongoing per-check cost that scales linearly with volume, and less control over exactly how a specific decision was reached — which matters for the explainability goals discussed in 17.3. Many production systems land on a hybrid: buying the commodity parts of identity proofing (basic document authenticity, liveness) while building the platform-specific parts in-house (deduplication against the platform’s own user base, eligibility rules, and fraud scoring that incorporates the platform’s own behavioural signals) — capturing much of the speed benefit of buying while keeping the most competitively important and platform-specific logic under direct control.

16.4 Key Trade-off: Centralised vs Federated Review Teams

A single, centralised human review team gives consistent decision quality and easier training and calibration, but can become a bottleneck during regional volume spikes and may lack the local-language and local-document familiarity needed for accurate review in every market. Regionally federated review teams scale more naturally with regional demand and bring valuable local context, but require more deliberate calibration work to keep decision standards consistent across regions, since a case that a lenient regional team would approve should not be one a stricter regional team would reject purely due to inconsistent local training rather than any real difference in the evidence.

17

Best Practices & Common Mistakes

17.1 Best Practices

  • Treat every raw document/selfie as a liability, not an asset — minimise retention aggressively.
  • Make rejection reasons genuinely useful to the applicant without revealing exact fraud-detection signals to attackers.
  • Continuously audit-sample the auto-approve lane, not just the rejected/reviewed lanes.
  • Design for regional data residency from day one — retrofitting it later is extremely costly.
  • Keep eligibility rules externally configurable so product changes don’t require a code deploy.
  • Build the appeals path as a first-class workflow, not an afterthought support ticket.

17.2 Common Mistakes

mistake

Treating This as a Pure ML Problem

Model accuracy matters, but an excellent model bolted onto a weak workflow (no dedup, no audit trail, no appeals) still produces a system that is easy to abuse and impossible to defend in a regulatory review.

mistake

One Global Threshold for Every Country

Document quality, common fraud patterns, and typical camera/network quality vary hugely by region. A single global risk threshold tends to either over-reject legitimate applicants in some regions or under-catch fraud in others; regional threshold tuning, validated with local audit samples, performs far better.

mistake

No Dedicated Abuse Team Feedback Loop

Fraud patterns evolve constantly. Without a direct, fast feedback loop from the fraud/trust-and-safety team back into the risk scoring model and rules engine, the system’s defences go stale within months.

17.3 Designing for Explainability

Every automated rejection and every automated approval should be explainable after the fact — not necessarily to the applicant in full detail, but internally, to a reviewer, an auditor, or a regulator asking “why was this decision made?” This means the risk-scoring model’s output should always be stored alongside the specific signal values that fed into it, not just the final number, and wherever possible the scoring approach should favour models whose decisions can be traced back to identifiable contributing factors over fully opaque black-box models, even at some cost to raw accuracy.

17.4 Building Trust with Gradual Rollouts

When introducing a new automated check for the first time, the safest rollout path is shadow mode first — running the new check on all live traffic, logging its output, but not letting it affect any real decision — followed by a limited-impact rollout where the new check can only push cases into human review (never a fully automated reject), and only once its behaviour is well understood over weeks of data does it earn the ability to drive a fully automated decision. Skipping straight to full automated authority for a brand-new check is one of the more common and costly mistakes in systems like this.

18

Real-World Examples

Large-scale identity verification challenges of this kind appear across several kinds of platforms:

case A

Social Media Verification

Confirms notable public figures and organisations are who they claim to be, largely to stop impersonation of celebrities, journalists, and brands.

case B

Ride-Sharing & Delivery Platforms

Verify driver/courier identity documents at onboarding and periodically thereafter, combining document checks with real-time selfie liveness checks before each shift in some markets.

case C

Fintech & Banking Apps

Run KYC identity verification as a legal requirement before allowing money movement, often the most mature and heavily regulated version of this pattern.

case D

Freelance & Marketplace Platforms

Verify sellers’ business registration to reduce buyer fraud and enable higher-trust transaction limits.

case E

Dating Apps

Use lightweight liveness-based “photo verification” matching a real-time selfie pose to profile photos as a much lower-friction cousin of full document-based identity proofing, aimed at catching catfishing rather than legal identity fraud.

Across all of these, the same architectural skeleton recurs: fast intake, asynchronous multi-stage automated pipeline, risk-based routing, a human review lane for the middle band, an append-only audit trail, and strict, minimal, time-bound retention of the underlying sensitive evidence.

💡
Production Example

Ride-sharing and delivery platforms operating in multiple countries commonly run periodic in-app selfie liveness checks before a driver or courier can go online for a shift, specifically to catch account-sharing — where the verified original driver lets a different, unverified person use their account — a fraud pattern that a one-time onboarding check alone cannot catch, which is why the re-verification and behavioural-trigger patterns from 6.2 and 5.12 matter as much as the initial check.

💡
Production Example

Payment and fintech apps subject to formal KYC regulation typically cannot rely purely on automated decisions for higher-risk account tiers; regulation in many jurisdictions requires a documented human review step and a clear audit trail for certain categories of financial account approval, which is part of why the human-review lane and the tamper-evident audit log described in 4.10 and 7.4 are treated as core requirements rather than optional nice-to-haves in this tutorial’s design.

18.1 What Differs Across These Use Cases

While the architectural skeleton is shared, the tuning differs significantly by use case. A dating app’s photo verification tolerates a relatively high false-reject rate, since the cost of a wrongly rejected genuine user is low (they can simply retry), and speed matters more than forensic rigour, since the badge is a low-stakes trust signal rather than a legal identity claim. A banking KYC flow tolerates almost no false accepts, since the regulatory and financial cost of onboarding a fraudulent account is severe, and is willing to accept significantly slower turnaround and heavier human review as the price of that caution. Recognising which end of this spectrum a given verified badge program sits on is one of the first and most consequential design decisions, and it should be made explicit and revisited periodically as the product and its stakes evolve.

19

Frequently Asked Questions

Q1Why not just use one big third-party identity verification vendor for everything?

Vendors are strong at generic document/biometric checks but rarely know your platform-specific eligibility rules, your existing user graph for deduplication, or your appeals process. Most production systems use one or more vendors as a component inside a larger, platform-owned orchestration layer rather than as the entire system.

Q2How do you handle countries with weak or inconsistent document standards?

Document-template matching is maintained per country/document-type, is updated as new templates are released, and for countries with very limited digitised ID infrastructure, systems often fall back to alternate verification paths such as a live video interview with a trained reviewer instead of relying purely on document forensics.

Q3What happens if the risk-scoring model is updated and it changes past decisions?

Model updates never retroactively change already-decided applications automatically. Instead, badge holders may be selected for the next scheduled re-verification cycle, and any bulk re-evaluation of previously approved badges under a new model is treated as its own carefully governed project with human oversight, not an automatic mass action.

Q4How is a “verified” badge different from a general account “trust score”?

A verified badge is a discrete, binary claim (“we confirmed this identity”), backed by a specific, auditable evidence bundle and decision. A trust score is typically a continuous, constantly-updating signal blending many behavioural factors over time. Some platforms use trust scores as one input into eligibility for a badge, but they serve different purposes and usually live in different systems.

Q5Can the applicant see their own risk score?

Generally no — the exact numeric score and the specific weighted signals behind it are kept internal, both to prevent gaming and because a raw score without full context can be misleading. Applicants instead receive a clear outcome and, on rejection, a general, actionable reason category plus an appeals path.

Q6How long should an applicant typically wait for a decision?

Auto-approved and auto-rejected cases can resolve within minutes, since the automated pipeline runs asynchronously but is not bottlenecked by human availability. Cases routed to human review typically take longer — anywhere from several hours to a few days depending on reviewer staffing and queue depth — which is why setting honest expectations in the product UI matters as much as the underlying engineering.

Q7What stops someone from just buying a stolen identity document and applying with it?

This is exactly what the liveness and face-match checks are designed to catch — a stolen document photo will not match a live selfie of the person actually holding the stolen document, unless the attacker also has a convincing way to impersonate the original document holder’s face in real time, which is precisely the harder problem that deepfake-resistant liveness detection is built to defend against.

Q8Does a rejected applicant get told exactly which document field failed?

No, and this is intentional, as covered in 5.5 — revealing the precise forensic signal that triggered a rejection would hand attackers a roadmap for producing a better fake on their next attempt. Rejected applicants instead get a general category of issue (for example, “document could not be verified”) along with clear instructions for what kind of evidence would help on resubmission or appeal.

20

Summary & Key Takeaways

Key Takeaways

  • A verified badge system is really five stacked problems: identity proofing, uniqueness, eligibility, fraud resistance, and fair appeals — not a single “verify the document” feature.
  • An asynchronous, fan-out/fan-in pipeline behind a queue keeps the system fast and resilient, even though individual verification checks can be slow and unreliable.
  • A three-lane decision model — auto-approve, human review, auto-reject — based on a combined risk score balances cost, speed, and accuracy far better than a single hard rule.
  • Raw documents, selfies, and biometric data are treated as high-liability assets: encrypted, access-controlled, minimally retained, and never mixed into general-purpose data stores or caches.
  • An append-only, tamper-evident audit log and a first-class appeals workflow are not optional extras — they are core to making the system fair, defensible, and regulator-ready.
  • Regional data residency, per-country threshold tuning, and continuous audit sampling of even the auto-approved lane are what separate a system that merely works from one that is trustworthy at global scale.
  • Every architectural choice in this system is ultimately a trade-off between the False Accept Rate and the False Reject Rate described in 3.10, and being explicit about which side of that curve the product prioritises — and why — should drive every threshold, every routing rule, and every escalation path built on top of it.
  • The badge itself is a small UI element, but the system behind it is a long-lived, continuously operating decision engine — issuance, monitoring, re-verification, and revocation are all part of the same lifecycle, not separate one-off features.
💡
Final Thought

Designing this kind of system well means treating trust as a first-class engineering concern with the same rigour normally reserved for correctness and performance — because for a verified badge program, trust is not a side effect of the architecture, it is the entire product.