Workshop
Things I built because the manual version was irritating.
Personal projects. Same way of looking at a problem as at work — just without a ticket queue.
01
ApplyFlow
Outreach that keeps going after you close the laptop
GitHub Repo →(Private — access provided on request)
Sending a lot of cold emails by hand was tedious. ApplyFlow takes a campaign — a list of roles, a resume, a schedule — and sends personalized mail in the background through the user's own Gmail.
You sign in with Google, upload a spreadsheet of companies and roles, attach a resume, and watch sent / failed / in-progress counts on a dashboard. You can cancel a campaign; pending mail stops.
Spreadsheet rows need Company Name, Role Name, and Company Mail ID. Headers are checked, emails are validated, and a file is capped at 400 rows. Duplicate recipients are skipped. One mail goes out every three minutes per campaign. Delivery is at-least-once, not exactly-once. Cancel marks the campaign stopped; it does not purge the queue. The dashboard polls about every four seconds.
Gallery
Tap a frame to open it.
Why the pieces exist
- React — campaigns, uploads, and progress. A SPA talking to the Go API. Local UI is Vite; production build is static files in S3 served by CloudFront.
- Go API — users, campaigns, templates, outreach state, and a /health check. It runs as a container on ECS Fargate in ap-south-2 (Hyderabad). The scheduler lives in the same process.
- Go worker — a second Fargate service. It pulls from SQS and sends the mail, so a slow Gmail call never sits on the HTTP request. Queue visibility is two minutes; failed sends can be retried.
- PostgreSQL on RDS — private in the VPC. Progress survives a refresh. The API reaches it through security groups, not a public database port. A stopped bastion host is only for a laptop SSH tunnel to the database.
- S3 — resumes and spreadsheets, separate from the database.
- SQS — the handoff between "this email is due" and "send it."
- In-process scheduler — campaign start times. A Go loop inside the API periodically claims due outreach and puts it on the queue. The queue is not the clock.
- Google OAuth and the Gmail API — mail goes out from the user's Gmail. Sign-in returns a token to the React app. Access and refresh tokens stay on the server.
- ECS Fargate — container orchestration for the API and worker services with auto-scaling and isolated execution environments.
- ECR — container registry storing Docker images tagged with git SHA for traceable deployments.
- Application Load Balancer — distributes traffic across API instances with health checks.
- CloudFront — CDN serving the React frontend with HTTPS termination and edge caching.
- CloudWatch — centralized logging and monitoring for all Fargate tasks.
- GitHub Actions with OIDC — CI/CD pipeline assuming IAM roles without stored credentials.
- Docker Compose & LocalStack — local development environment mimicking AWS services.
- AWS CLI — infrastructure provisioning for S3 buckets and SQS queues.
Let's connect
To discuss more about this system or the engineering decisions behind it, let us connect.
Connect with me