← Course Index | WebhookFlow — Teaching Project
All Weeks
Course Teaching Project · All 8 Weeks

Build WebhookFlow
A Real-World Webhook Platform

One project, all course concepts. We start with plain Java classes and grow this into a production-grade microservices platform on Azure Kubernetes — step by step, week by week.

☕ Java + Spring Boot 🗄️ PostgreSQL + JPA 🏗️ Microservices 🐳 Docker + Kubernetes ☁️ Azure ⚛️ React Dashboard 🤖 AI/LLM
🔔
Project Overview
What is WebhookFlow?
A webhook event receiving, routing, and delivery platform
Simple analogy: Imagine a postal sorting office. Letters (webhook events) arrive from different senders (Razorpay, GitHub, Stripe). The sorting office validates the letter is genuine, logs its arrival, reads the address, and delivers it to the right recipient. If delivery fails, it retries. WebhookFlow is that sorting office — built in Java and deployed on the cloud.

A webhook is an HTTP POST request that one system sends to another when something happens. Razorpay sends a webhook when a payment succeeds. GitHub sends one when code is pushed. Zomato sends one when an order status changes.

WebhookFlow is the platform that sits in the middle — it receives these events, validates them, stores them, and forwards them to whichever internal services need to act on them.

The problem it solves

ProblemHow WebhookFlow solves it
Multiple services receive the same eventOne receiver → fan out to all subscribers via queue
External system sends event but our service is downEvent stored in DB; retry with exponential backoff
Fake / tampered events from malicious sourcesHMAC-SHA256 signature validation on every request
No visibility into what events arrived and whenFull event log, delivery status, and analytics dashboard
Single webhook handler becomes a bottleneck under loadHorizontally scale receiver pods on Kubernetes
🌍
Real World Context
Why Webhooks? Who Uses Them?
Every company you know sends and receives webhooks every second
Company / APIEventWebhook Payload ExampleAction Triggered
Razorpaypayment.capturedorderId, amount, method, userIdMark order as paid, send invoice email
Razorpaypayment.failedorderId, errorCode, reasonNotify customer, unlock cart, alert team
GitHubpushrepo, branch, commits, authorTrigger CI/CD pipeline (GitHub Actions)
GitHubpull_request.openedtitle, author, base branchRun code review bot, assign reviewers
Stripeinvoice.payment_failedcustomerId, amount, retryCountSuspend subscription, send dunning email
Zomato / Swiggyorder.status_changedorderId, status, driverId, etaPush notification to customer app
Twiliomessage.receivedfrom, body, mediaUrlProcess incoming SMS, run chatbot
Your companyuser.registereduserId, email, planSend welcome email, setup trial, log analytics
Interview insight: When an interviewer asks "how would you design a payment notification system?" — the answer involves webhooks. Building WebhookFlow means you can answer this question with a real architecture you've built yourself.
🚀
Feature List
What the Completed System Does
Every feature maps to a course week
📥 Receive
  • Accept POST /webhook/receive/{source}
  • HMAC-SHA256 signature validation
  • Parse JSON payload with Jackson
  • Return 200 OK immediately (async)
  • Idempotency — ignore duplicate events
💾 Persist
  • Save every event to PostgreSQL
  • Track status: RECEIVED → PROCESSING → DELIVERED / FAILED
  • Full delivery log per attempt
  • Indexed queries by event type, status, date
  • Retention policy (auto-delete after 90 days)
🔀 Route & Deliver
  • Match event type to registered subscriptions
  • Forward event via HTTP POST to subscriber URL
  • Retry failed deliveries (3 attempts)
  • Exponential backoff: 1s → 5s → 30s
  • Dead letter queue for permanently failed events
📊 Monitor
  • React dashboard with live event feed
  • Delivery success rate per subscription
  • Event volume charts (hourly/daily)
  • Failed event list with retry button
  • AI: natural language event queries
🔒 Secure
  • HMAC-SHA256 signature verification per source
  • Secrets stored in Azure Key Vault
  • Rate limiting on receiver endpoint
  • IP allowlist per source (optional)
⚡ Scale
  • Stateless receiver — scale horizontally
  • Azure Service Bus decouples receive from process
  • Kubernetes HPA scales receiver pods with load
  • Redis cache for subscription lookup (fast)
🏗️
Design
System Architecture
High-level view of all services and how they connect
🌐
External Systems
Razorpay · GitHub · Stripe
Any HTTP client
🚪

API Gateway

Route · Auth · Rate limit
Port 8000 (AKS)

↓ routes to
📥

webhook-receiver

Validate signature
Parse + save event
Publish to queue
Port 8080

⚙️

event-processor

Read from queue
Match subscriptions
HTTP deliver to targets
Retry on failure · Port 8081

📧

notification-service

Email alerts (SES)
SMS via Twilio
Slack notifications
Port 8082

↓↑ shared infrastructure
📨

Azure Service Bus

Message queue between receiver and processor
Guarantees delivery

🗄️

PostgreSQL / Azure SQL

webhook_events
subscriptions
delivery_logs

Redis Cache

Subscription lookup cache
Idempotency keys
Rate limit counters

↕ client-facing
⚛️

webhook-dashboard

React + TypeScript
Live event feed · Subscription manager
Analytics charts · Port 3000

🤖

AI Service

LangChain4j + Azure OpenAI
Natural language queries
Event classification · Port 8083

Development progression: We start with only webhook-receiver as a single Spring Boot app with an H2 database. Each week, we add more services and infrastructure until we have the full architecture above running on AKS.
🔄
Deep Dive
Request Lifecycle — What Happens When a Webhook Arrives
Trace a Razorpay payment.captured event from entry to delivery
Scenario: Rahul just paid ₹2,499 for a course on your platform. Razorpay sends a payment.captured webhook to your server. Here is every step that follows.
1

Razorpay sends HTTP POST

Razorpay makes a POST request to https://webhookflow.yourdomain.com/api/events/receive/razorpay with a JSON body and an X-Razorpay-Signature header.

POST /api/events/receive/razorpay X-Razorpay-Signature: sha256=abc123xyz... Content-Type: application/json { "event": "payment.captured", "payload": { "payment": { "entity": { "id": "pay_OEjVXL7TdMmfHs", "amount": 249900, "currency": "INR", "method": "upi", "contact": "+919876543210", "email": "rahul@email.com", "order_id": "order_OEjVUz45bQ7qsn" } } }, "created_at": 1701936000 }
webhook-receiver
2

Signature Validation

The receiver computes HMAC-SHA256 of the raw request body using the Razorpay secret key (fetched from Azure Key Vault) and compares it to the header value. If they don't match → reject immediately with 401. This prevents fake events from malicious actors.

If the eventId was already processed → return 200 OK immediately (idempotent) — no duplicate processing.

webhook-receiver · SignatureValidator.java
3

Parse & Map to Domain Object

Jackson deserializes the JSON body. The receiver maps it to a WebhookEvent Java object with status = RECEIVED.

WebhookEvent event = new WebhookEvent(); event.setSource("razorpay"); event.setEventType("payment.captured"); event.setPayload(rawBody); event.setStatus(EventStatus.RECEIVED); event.setReceivedAt(LocalDateTime.now());
webhook-receiver · WebhookController.java
4

Save to Database

The event is persisted to the webhook_events table in PostgreSQL via Spring Data JPA. This is the safety net — even if everything downstream crashes, the event is safe and can be reprocessed.

PostgreSQL · WebhookEventRepository.java
5

Publish to Message Queue

The event ID is published to an Azure Service Bus queue. This decouples receiving from processing — the receiver's job is done and it returns immediately.

Azure Service Bus · EventPublisher.java
6

Return 200 OK to Razorpay — Immediately

Razorpay (and all webhook senders) expect a fast 200 OK response. If your server takes >5 seconds, they consider it failed and retry. By returning 200 right after saving to DB + queue, we guarantee fast response regardless of how long downstream processing takes.

Best Practice: Accept Fast, Process Async
7

event-processor Picks Up from Queue

The event-processor service is listening to the Service Bus queue. It picks up the event ID, fetches the full event from DB, and updates status to PROCESSING.

event-processor · EventProcessorService.java
8

Match Subscriptions

The processor queries the subscriptions table for all active subscriptions that listen to payment.captured events. Typically: Order Service, Email Service, Analytics Service.

SELECT * FROM subscriptions WHERE is_active = true AND (event_types LIKE '%payment.captured%' OR event_types LIKE '%payment.*%')
event-processor · SubscriptionMatcher.java
9

Deliver to Each Subscriber

For each matching subscription, the processor makes an HTTP POST to the subscriber's targetUrl with the event payload. A DeliveryLog entry is saved for each attempt — recording the response status, body, and timestamp.

If the subscriber returns 2xx → success. Anything else → retry.

event-processor · DeliveryService.java
10

Retry on Failure (Exponential Backoff)

If delivery fails, the processor waits and retries:

  • Attempt 1 — immediate
  • Attempt 2 — wait 1 second
  • Attempt 3 — wait 5 seconds
  • Attempt 4 — wait 30 seconds
  • After 4 failures — status = FAILED, event goes to dead-letter queue, team alerted
event-processor · RetryScheduler.java
11

Notification Service Triggered

For a payment.captured event: send a confirmation email to Rahul ("Your payment of ₹2,499 was successful") and trigger a Slack alert to the operations team. The notification-service listens on a separate Service Bus topic.

notification-service · EmailService.java
12

Dashboard Updates

The React dashboard polls GET /api/events?limit=20 every 5 seconds. The new event appears in the live feed with status DELIVERED and green badge. Rahul's payment is visible in the events log.

React Dashboard · EventFeed.tsx
🗄️
Domain Model
Core Data Model — The 4 Entities
These are the Java classes and database tables we build first
📄 WebhookEvent
LongidPK, auto-gen
Stringsourcerazorpay / github
StringeventTypepayment.captured
StringexternalIdfor idempotency
Textpayloadraw JSON body
EnumstatusRECEIVED→DELIVERED
intretryCount0 to 4
DateTimereceivedAtindexed
DateTimeprocessedAtnullable
📋 WebhookSubscription
LongidPK
StringnameOrder Processor
StringtargetUrlhttps://myapp.com/
StringeventTypesCSV or JSON array
StringsecretKeyfor signing outgoing
booleanisActiveenable / disable
intmaxRetriesdefault: 4
DateTimecreatedAtaudit
📊 DeliveryLog
LongidPK
FKeventId→ WebhookEvent
FKsubscriptionId→ Subscription
intattemptNumber1, 2, 3, 4
intresponseStatus200 / 500 / 0
StringresponseBodytruncated 500 chars
booleansuccesstrue if 2xx
longdurationMslatency
DateTimedeliveredAtindexed
🔑 EventSource
LongidPK
Stringnamerazorpay / github
StringsecretKeyHMAC validation key
StringsignatureHeaderX-Razorpay-Signature
StringipAllowlistoptional CSV
booleanisActiveenable / disable
DateTimecreatedAtaudit

EventStatus Enum

public enum EventStatus { RECEIVED, // just arrived, saved to DB PROCESSING, // picked up by event-processor DELIVERED, // successfully forwarded to all subscribers PARTIAL, // delivered to some subscribers, failed for others FAILED, // all delivery attempts exhausted IGNORED // no active subscriptions matched this event type }
🔗
REST API
Complete API Design
All endpoints we build — grouped by resource

Event Ingestion

POST /api/events/receive/{source} Receive incoming webhook from external source Week 2

Event Management

GET /api/events List events (filter: ?source=razorpay&status=FAILED&from=2025-01-01) Week 3
GET /api/events/{id} Get single event with full payload and delivery logs Week 3
POST /api/events/{id}/retry Manually trigger re-delivery of a failed event Week 4
GET /api/events/{id}/deliveries All delivery attempts for an event (logs, latency, status) Week 3

Subscription Management

POST /api/subscriptions Register a new subscriber endpoint Week 3
GET /api/subscriptions List all subscriptions Week 3
PUT /api/subscriptions/{id} Update subscription (targetUrl, eventTypes) Week 3
PATCH /api/subscriptions/{id}/toggle Enable or disable subscription Week 3
DELETE /api/subscriptions/{id} Remove a subscription Week 3

Event Sources

POST /api/sources Register a new webhook source (Razorpay, GitHub, custom) Week 3
GET /api/sources List all registered sources Week 3

Analytics

GET /api/analytics/summary Event counts by type, status, source (last 24h) Week 5
GET /api/analytics/delivery-rate Success % per subscription, avg latency Week 5
GET /api/analytics/volume?interval=hourly Event volume timeline — powers the dashboard chart Week 7

AI Queries

POST /api/ai/query Natural language: "show failed payments in the last hour" Week 8
POST /api/ai/classify/{eventId} AI classifies event intent: PAYMENT / AUTH / NOTIFICATION Week 8
📅
Build Roadmap
Week-by-Week Build Plan
What we build each week — mapped to course topics
Pre-Course Setup & Orientation — VS Code · JDK · Maven · Git
📚 Topics
  • Install VS Code, JDK 21, Maven
  • VS Code Spring Boot extensions
  • Git init, first commit, GitHub repo
🔨 What we build
  • Create GitHub repo: webhookflow
  • Generate Spring Boot project (start.spring.io)
  • First git push of the skeleton project
  • H2 in-memory DB connected and running
Week 1 Java Core & OOP — Variables · Loops · Classes · OOP · Collections · Java 8
📚 Topics
  • Variables, conditions, loops, methods
  • Classes, constructors, this keyword
  • Inheritance, polymorphism, interfaces
  • ArrayList, HashMap, for-each
  • Lambda, Stream API, Optional
  • Exception handling, custom exceptions
🔨 What we build
  • WebhookEvent.java — class, fields, constructor
  • EventStatus.java — enum (RECEIVED, DELIVERED, FAILED)
  • WebhookSubscription.java — domain model
  • DeliveryLog.java — nested object
  • InMemoryEventStore.java — ArrayList store, search with Streams
  • SignatureValidator.java — utility with static methods
  • EventNotFoundException.java — custom exception
Week 2 Spring Boot Basics — Annotations · DI · REST Controller · application.properties
📚 Topics
  • @SpringBootApplication, @RestController
  • @Service, @Component, constructor DI
  • @PostMapping, @GetMapping, @RequestBody
  • application.properties configuration
  • Spring Boot DevTools auto-restart
🔨 What we build
  • WebhookController.java — first POST endpoint
  • EventService.java — business logic layer with @Service
  • SignatureService.java — HMAC validation, injected into controller
  • Configure server.port, application name in properties
  • Test POST /api/events/receive/razorpay in Postman
  • See "Event received!" response — milestone ✅
Week 3a Spring Data JPA — @Entity · Repository · CRUD · Custom Queries
📚 Topics
  • @Entity, @Id, @Column, @Enumerated
  • JpaRepository<T, ID>
  • findByX(), @Query with JPQL
  • @OneToMany, @ManyToOne relationships
  • Lazy vs Eager loading
🔨 What we build
  • Add @Entity to WebhookEvent, WebhookSubscription, DeliveryLog
  • WebhookEventRepository.java — extends JpaRepository
  • findByStatus(), findBySourceAndEventType(), findByReceivedAtAfter()
  • DeliveryLog @ManyToOne → WebhookEvent relationship
  • Events now persist to H2 → switch to PostgreSQL
Week 3b REST API Design & SQL — Full CRUD · Error Handling · Schema Design · Indexes
📚 Topics
  • HTTP methods, status codes, URL design
  • @RestControllerAdvice, @ExceptionHandler
  • @Valid, @NotNull, @Size validation
  • DB schema design, indexes, transactions
  • Postman collections, automated tests
🔨 What we build
  • Full CRUD REST API for Subscriptions
  • GlobalExceptionHandler.java — returns clean JSON errors
  • SubscriptionRequest.java — DTO with @Valid annotations
  • DB indexes on event_type, status, received_at (query speed)
  • Postman collection with 12 requests + tests
Week 4 Microservices — Split · Service Contracts · Async Communication
📚 Topics
  • Single-process service vs distributed platform tradeoffs
  • Service boundaries — what to split
  • Synchronous (REST) vs async (queue)
  • Spring RestTemplate / WebClient
  • Azure Service Bus integration
🔨 What we build
  • Extract event-processor-service as a new Spring Boot project
  • EventPublisher.java — publish to Azure Service Bus on receive
  • EventConsumer.java — @ServiceBusListener in processor service
  • Receiver returns 200 OK instantly; processor runs async
  • Both services share the same PostgreSQL database
Week 5 Azure Cloud — App Service · Azure SQL · Key Vault · Service Bus
📚 Topics
  • Azure Portal: Resource Groups, App Service
  • Azure SQL Database (managed PostgreSQL)
  • Azure Key Vault — secret management
  • Azure Service Bus queues and topics
  • Azure CLI deployment commands
🔨 What we build
  • Deploy webhook-receiver to Azure App Service
  • Move DB from local PostgreSQL → Azure SQL
  • Store HMAC secret keys in Azure Key Vault
  • Service Bus queue created in Azure portal
  • First live webhook test: Razorpay → Azure → DB ✅
Week 5 Docker & Containers — Dockerfile · docker-compose · Container Registry
📚 Topics
  • Dockerfile for Spring Boot (multi-stage build)
  • docker-compose for local dev environment
  • Environment variables in containers
  • Push image to Azure Container Registry
🔨 What we build
  • Dockerfile for webhook-receiver and event-processor
  • docker-compose.yml — receiver + processor + PostgreSQL + Redis
  • One command: docker compose up → full stack running locally
  • Push images to Azure Container Registry (ACR)
Week 6 Kubernetes + Git/GitHub CI/CD — AKS · HPA · GitHub Actions
📚 Topics
  • Kubernetes Deployments, Services, ConfigMaps, Secrets
  • HorizontalPodAutoscaler (HPA)
  • Rolling update deployments
  • GitHub Actions CI/CD pipeline
  • kubectl commands for operations
🔨 What we build
  • k8s/receiver-deployment.yaml — 2 replicas, readiness probe
  • k8s/processor-deployment.yaml
  • HPA for receiver: scale 2→10 pods when CPU > 70%
  • GitHub Actions pipeline: push code → run tests → build Docker → push ACR → deploy to AKS
  • Full production deployment on AKS ✅
Week 7 React + TypeScript Frontend — Components · Hooks · API Integration · Live Feed
📚 Topics
  • React components, props, state
  • useState, useEffect hooks
  • TypeScript interfaces for API responses
  • Calling REST APIs with fetch / axios
  • React Query for data fetching + caching
🔨 What we build
  • EventFeed.tsx — live list, auto-refreshes every 5s
  • EventDetail.tsx — drawer with raw JSON, delivery logs, retry button
  • SubscriptionManager.tsx — CRUD UI for subscriptions
  • AnalyticsDashboard.tsx — event volume chart, delivery rate cards
  • Status badges: 🟢 DELIVERED · 🔴 FAILED · 🟡 PROCESSING
Week 8 AI & LLM Integration — LangChain4j · Azure OpenAI · Classification · NL Queries
📚 Topics
  • What is an LLM / embedding / RAG
  • LangChain4j in Spring Boot
  • Prompt engineering for structured output
  • Function calling / tool use
  • Azure OpenAI vs OpenAI API
🔨 What we build
  • EventClassifier.java — AI classifies payload: PAYMENT / AUTH / NOTIFICATION
  • AiQueryController.java — POST /api/ai/query: "show failed payments last hour" → real DB results
  • AI generates human-readable event summary: "₹2,499 UPI payment by Rahul captured"
  • Anomaly detection: alert if failure rate > 20% in 10 min window
  • AI Q&A on dashboard: type a question, get an answer
🏗️
Architecture Evolution
WebhookFlow Microservice → Distributed Webhook Platform
WebhookFlow is a microservice from day one — it grows from one process to multiple internal services

Phase 1 — Weeks 1–3: WebhookFlow Microservice (single process)

WebhookFlow is already a microservice in Phase 1 — it has a single responsibility (webhook platform) and other applications call it via HTTP. It runs as one Spring Boot process with one database. "Single process" does not mean "monolith" — a monolith is when unrelated domains (orders, users, payments, webhooks) are all forced into one codebase. WebhookFlow is a dedicated service from the start.
webhookflow/ ├── src/main/java/com/campustoai/webhookflow/ │ ├── WebhookFlowApplication.java │ ├── controller/ │ │ ├── WebhookController.java ← POST /api/events/receive/{source} │ │ └── SubscriptionController.java ← CRUD /api/subscriptions │ ├── service/ │ │ ├── EventService.java ← receive, validate, save │ │ ├── DeliveryService.java ← deliver to subscribers, retry │ │ └── SignatureService.java ← HMAC validation │ ├── repository/ │ │ ├── WebhookEventRepository.java │ │ ├── SubscriptionRepository.java │ │ └── DeliveryLogRepository.java │ ├── model/ │ │ ├── WebhookEvent.java │ │ ├── WebhookSubscription.java │ │ ├── DeliveryLog.java │ │ └── EventStatus.java │ └── exception/ │ ├── EventNotFoundException.java │ └── InvalidSignatureException.java └── pom.xml

Phase 2 — Weeks 4–6: Split into Services

We extract the delivery logic into a separate service. The two services communicate via Azure Service Bus (async), not direct HTTP calls. Each service has its own codebase and can be deployed independently.
ServiceResponsibilitiesTriggersPort
webhook-receiverAccept POST, validate signature, save event, publish to queue, return 200External HTTP8080
event-processorRead from queue, match subscriptions, deliver, save delivery log, retryService Bus queue8081
notification-serviceSend email/SMS for critical events (payment failures, alerts)Service Bus topic8082

Phase 3 — Weeks 7–8: Full Production System

React frontend added. AI service added. GitHub Actions automatically deploys to AKS on every push to main. The webhook-receiver can auto-scale from 2 to 10 pods under high load.
ServiceLanguage/TechDeploymentScales
webhook-receiverSpring Boot 3 + Java 21AKS — 2 podsYes — HPA 2→10
event-processorSpring Boot 3 + Java 21AKS — 1 podManual
notification-serviceSpring Boot 3 + Java 21AKS — 1 podNo
ai-serviceSpring Boot + LangChain4jAKS — 1 podNo
webhook-dashboardReact 18 + TypeScriptAzure Static Web AppsCDN
PostgreSQLAzure SQL (managed)Azure — PaaSAzure managed
RedisAzure Cache for RedisAzure — PaaSAzure managed
⚙️
Tools & Technologies
Complete Tech Stack
Every technology used — and which week you learn it
CategoryTechnologyUsed for in WebhookFlowWeek
LanguageJava 21 (LTS)All backend servicesWeek 1
FrameworkSpring Boot 3.xREST API, DI, auto-configWeek 2
ORMSpring Data JPA + HibernateDatabase access layerWeek 3
DatabasePostgreSQL / Azure SQLPersist all events, subscriptions, logsWeek 3
CacheRedis (Azure Cache)Subscription lookup, idempotency keysWeek 4
MessagingAzure Service BusDecouple receiver from processorWeek 4
Secret MgmtAzure Key VaultHMAC secrets, DB passwordsWeek 5
MonitoringAzure Application InsightsRequest tracing, error alertsWeek 5
ContainerDockerPackage each service as a containerWeek 5
OrchestrationKubernetes / AKSDeploy and scale all servicesWeek 6
CI/CDGitHub ActionsAutomated test → build → deploy pipelineWeek 6
FrontendReact 18 + TypeScriptEvent dashboard, subscription UIWeek 7
Frontend StateReact Query (TanStack)Data fetching, caching, auto-refreshWeek 7
StylingTailwind CSSDashboard UIWeek 7
AI/LLMLangChain4j + Azure OpenAIEvent classification, NL queriesWeek 8
BuildMavenDependency management, packagingPre-Course
IDEVS Code + ExtensionsDevelopment environmentPre-Course
API TestingPostmanTest all REST endpointsWeek 2
Version ControlGit + GitHubSource code, PRs, historyPre-Course
Portfolio outcome: By the end of Week 8, students have a production-deployed, AI-enabled microservices platform running on Azure Kubernetes — available at a public URL, with a live React dashboard, fully CI/CD automated. This is a project that stands out in any fresher or 0–2 year experience interview.