Work

Systems I've shipped — from AI pipelines to full-stack products.

Each case study covers the problem, the approach, the architecture, and the outcome. No screenshots without context.

Problem

Off-the-shelf chatbots either hallucinated citations or dropped context across multi-step queries. Faculty needed answers they could trust enough to publish in a course handbook.

Approach

Hybrid retrieval (BM25 + dense embeddings), cross-encoder reranking, then a constrained generator that refuses to answer when grounded confidence drops below threshold. State managed through a LangGraph state machine with checkpointing for replay.

Architecture
  • 01Ingestion → chunking with semantic boundaries → embeddings (OpenAI text-embed-3) → pgvector
  • 02Hybrid retriever combines lexical and dense recall, top-k = 20
  • 03Cross-encoder rerank narrows to top-8 with calibrated scores
  • 04Generator runs through LangGraph with deterministic refusal node
  • 05Postgres-backed checkpointer for conversation state and audit
Outcome
94%
Citation accuracy
1.8s
p95 latency
12k+
Sources indexed
Problem

Faculty couldn't answer 'how is each team doing?' without manual chasing. Students didn't know what 'on track' looked like. Reviewers received PDFs with no context.

Approach

Built a structured workspace around project lifecycle (proposal → milestones → review). Layered AI-assisted progress summaries that read commit history and standup notes to produce honest weekly updates faculty actually read.

Architecture
  • 01Django REST backend with role-based permissions (student / mentor / reviewer)
  • 02React frontend with optimistic UI for status updates
  • 03Background jobs summarize weekly activity per team
  • 04Notification system routes only the changes that matter
Outcome
40+
Active project teams
~6 hrs
Faculty time saved / week
Department-wide
Adoption
Problem

Marketing teams were losing high-intent leads to slow, manual triage. Enrichment lived in one tool, the CRM in another, outreach in a third. By the time a salesperson saw a fresh inquiry, the moment had passed and the first message was always generic.

Approach

Modeled the entire funnel as a single n8n workflow — webhook ingestion, AI-driven enrichment and qualification, HubSpot CRM sync, and LLM-generated personalized outreach — with retry handling, structured logging, and validation at every step so the pipeline survives real production load.

Architecture
  • 01n8n webhook trigger fires on every new lead submission and validates the payload before anything else runs
  • 02Enrichment step pulls company and contact data from external APIs, then an LLM scores intent, business relevance, and engagement potential
  • 03Qualified leads are pushed into HubSpot CRM — contact records, deal stages, and activity logs created automatically with no manual entry
  • 04AI generates personalized email sequences using the enriched profile; stage-aware drip campaigns are triggered based on lead behavior
  • 05Follow-up reminders, engagement tracking, and high-priority sales-team notifications routed through the same workflow
  • 06Modular sub-workflows with retries, structured logs, and input validation — designed for real-world ops, not demos
Outcome
Reduced
Manual sales ops
Near-instant
Lead response time
Fully automated
Pipeline tracking
Deep dive

Read the full n8n + HubSpot AI sales pipeline case study

See more
Problem

Marketing ops needed Slack alerts when contacts hit specific points in a journey (form submits, churn risk, high-value triggers). The default SFMC stack has no native Slack step, and exporting to a middleware tool added latency and a paid hop the team didn't want.

Approach

Built a self-hosted Node.js custom activity exposing the four Journey Builder lifecycle endpoints (save, validate, publish, execute), registered it in SFMC Installed Packages with the right scopes, and wired execute() to fan contact data out through Slack Incoming Webhooks with channel + message templating configured per journey step.

Architecture
  • 01Node.js / Express API hosting the custom activity UI (config.json + HTML config screen) and the four lifecycle endpoints
  • 02Lifecycle endpoints implemented: /save, /validate, /publish, /execute — /execute receives the contact payload from Journey Builder at runtime
  • 03Activity registered in SFMC Setup → Installed Packages with a Journey Builder Activity component pointing at the hosted endpoint URLs
  • 04JWT verification on incoming requests using the package's signing secret to confirm calls actually originate from SFMC
  • 05Per-step config (Slack webhook URL, channel, message template with merge fields) persisted in the activity's inArguments and replayed on every contact
  • 06Slack delivery via Incoming Webhooks with templated blocks; failures surfaced back to Journey Builder via non-200 responses for retry
Outcome
4 / 4
Lifecycle endpoints
<2s
Alert latency
In production
Status
Deep dive

Read the full SFMC custom activity tutorial

See more

Want the architecture diagrams?

Happy to walk through any of these systems in detail — including trade-offs, failure modes, and what I'd do differently next time.

Get in touch