← Projects

Customer Onboarding App

A serverless identity-verification (KYC) pipeline for a bank — no person in the loop on the happy path.

A new customer submits their application details, a selfie, and a photo of their driver's license. The backend matches the selfie to the license photo, checks the license details against what they typed in, validates the license number with a third-party service, and records every outcome in DynamoDB.

I built the first version as a single Lambda that did everything, then refactored it into seven single-purpose functions orchestrated by Step Functions. Each function has its own least-privilege IAM role, so a failure points at one step instead of the whole pipeline, and the workflow only continues when both identity checks pass.

Architecture

S3 uploadzipped/
trigger→
Step FunctionsX-Ray traced
parallel→
Rekognition + Textractface + 8-field check
both pass→
SQS → API GatewayDLQ after 5 receives

Results land in DynamoDB; any failed check fires a KMS-encrypted SNS alert.

How a submission flows

  1. Unzip — Triggered by an S3 upload. Extracts the customer's zip (details CSV, selfie, license photo) and starts the state machine.
  2. WriteDynamo — Reads the details CSV and writes the customer record to DynamoDB.
  3. CompareFaces — Rekognition compares the selfie to the license photo at an 80% similarity threshold.
  4. CompareDetails — Textract AnalyzeID reads the license and checks 8 fields — name, date of birth, address, document number — against the application.
  5. SendToQueue — Runs only if both checks pass. Puts the license number on an SQS queue.
  6. SubmitLicense — Triggered by the queue. Calls the third-party validation API through API Gateway and records the result.

Design decisions

  • Step Functions holds the order of steps, the parallel branch, and the “only continue if both checks pass” rule in one definition instead of spreading it across function code.
  • SQS in front of the third-party call keeps a slow or failing provider from blocking identity verification; the dead-letter queue catches messages that keep failing so nothing is lost.
  • X-Ray on every function — enabling it only on the state machine gave an empty map. Tracing everywhere produces one trace that follows a request across the whole pipeline.
  • Everything as code — one SAM template defines the stack: HTTPS-only S3, DynamoDB auto scaling (2–20 units at 70%), the state machine, queues, API, and a separate inline-policy role per function.