Case studies/Products I built for myself
My product · AI assistant and lead intelligence

AdPal

2023 - 2025

AI chatbot and lifecycle-engagement platform for B2B SaaS and fintech: multi-tenant RAG over customer knowledge bases, OAuth-connected CRM and ad-platform pipelines.

Overview

AdPal is an AI assistant and lead-intelligence platform for B2B SaaS, fintech and service businesses. Its assistant, Clara, is trained on a company's website, documents and files, adapts its answers to the page and campaign a visitor came from, qualifies the conversation and captures contact details. Qualified chats land in a unified inbox with the visitor's journey, UTM data and a short summary, so a sales team can see which channels bring real leads.

The first generation of the portal ran on Laravel 8 with an Angular 15 client and ClickHouse for analytics. At the end of 2023 the product moved to a single Next.js 14 codebase, which carried the main build through 2024 and 2025: the chatbot builder, retrieval over customer knowledge, messenger channels, attribution reports, billing and a public API. Around the portal sit a Shopify app, a standalone chat widget, an Expo mobile app and WordPress tracking plugins. Around that portal grew a set of services rather than one application: tracking, bots, retrieval, ad automation and the infrastructure to run them.

Tracking is its own stack, because attribution is only as good as the data under it: a browser SDK bundled for the page, an ingest API and a backend that writes events into ClickHouse through Kafka and Redis, with SQS for hand-off, MaxMind GeoIP for location and user-agent parsing for device. That is what makes the source, campaign, keyword and device reports in the portal answerable.

Retrieval moved into a dedicated Python service: FastAPI with Pydantic models, a Qdrant vector database, OpenAI embeddings with a local sentence-transformers fallback when the API is not the right answer, and a PDF parser on PyMuPDF that pulls images and tables out of documents rather than flattening them to text. Heavy work runs as a Lambda worker off an SQS queue, infrastructure comes up through AWS CDK, and the whole thing runs locally against LocalStack so a developer does not need a cloud account to work on it.

The channels are a NestJS service of their own - Telegram through Telegraf, WhatsApp Business, Slack events and web APIs, plus WebSockets for live chat - over Sequelize with MySQL and Postgres alongside ClickHouse. Ad automation sits on the backend monorepo: site audits with Lighthouse, Wappalyzer and metascraper, the Facebook Business SDK, and Python services for Facebook campaigns, Google Ads campaigns and products, and a Performance Max autopilot.

The platform side is deliberately boring in the right places and specific where it counts: Serverless Framework and AWS CDK for the services, MSK for Kafka, a ClickHouse cluster with its own autoscaling stack, and a Kubernetes layout running ClickHouse through its operator with Keeper alongside Redpanda for streaming. Fingerprinting and visitor identification have their own small services, and a SIP edge covers telephony.

AdPal combined multi-tenant retrieval, OAuth-connected ad and CRM data and ClickHouse event analytics in one product, and its architecture lessons went straight into AEO Content. The AdPal line also includes AdPal Support, an embeddable assistant for Electro-Mech product support that answers from manuals, website content and support knowledge using Claude, Voyage AI embeddings and Turbopuffer hybrid search.

What was built

Stack

Frontend

Next.js 14
Main portal on the Pages Router: dashboards, bot builder, inbox, analytics, settings and API routes.
Mantine
Component library shared by the portal, the Shopify app and the Clara chat widget.
Angular 15
Client of the first-generation portal, before the move to a single Next.js codebase.
Expo / React Native
AdPal mobile app for iOS and Android with sign-in, tracked sites and page analytics screens.
TipTap, MDXEditor, CodeMirror
Content, markdown and code editing inside the portal
React Flow + dagre
Scenario and flow graphs with automatic layout
react-grid-layout
Draggable, resizable dashboard tiles
react-hook-form + zod
Forms and schema validation
TanStack Query
Server state, caching and refetching
i18next
Localization with chained http and local-storage backends

Backend

Laravel 8 (PHP)
First-generation REST API with HubSpot sync, LTV reports, webhooks and SQS messaging.
Decorator-based controllers
Portal API pattern with guard, role and HTTP-method decorators over server-side actions.
Socket.IO
Real-time channel between the portal server and connected browser clients.
NestJS
Channel service for Telegram, WhatsApp and Slack, and the CRM apps
FastAPI (Python)
Retrieval service with Pydantic models and a Lambda worker
Express
Tracking ingest API and tracking backend
Serverless Framework
Deployment of the backend monorepo, with Fargate, webpack, offline and monorepo plugins

Data

ClickHouse
Main store for visitor sessions, events, leads, integrations and chatbot embeddings, run as a replicated cluster.
Kafka
Asynchronous event stream for emails, webhooks and CRM synchronization.
Redis
Key-value store holding domain-keyed tracker data that the portal reads and updates.
Supabase
Postgres and Google sign-in for AdPal Support, plus lead tables for messenger bots in the portal.
Qdrant
Vector database behind the Python retrieval service
MaxMind GeoIP2
Location lookups on tracking events
Sequelize (MySQL, Postgres)
Relational storage behind the channel service
Amazon SQS
Queue between the API and the workers

AI

Anthropic Claude
Chat provider through the Anthropic API and AWS Bedrock, including Bedrock vision for image training data.
OpenAI
Alternative chat and embedding provider, routed alongside Claude by the portal's AI controller.
LlamaIndex
Workflow and OpenAI integration layer behind the retrieval-augmented chat provider.
Voyage AI + Turbopuffer
Embeddings and hybrid search over manuals and support knowledge in AdPal Support.
AWS Bedrock
Model access alongside the Anthropic and OpenAI SDKs
sentence-transformers
Local embedding fallback in the Python retrieval service
PyMuPDF
PDF parsing that keeps images and tables instead of flattening to text

Infrastructure & DevOps

AWS CDK
Infrastructure as code for the portal, the Shopify app and the chat widget.
ECS Fargate
Containerized portal services behind an Application Load Balancer with CPU-based autoscaling.
AWS Lambda
Generates per-company tracker script settings, invoked from the portal when a website is configured.
S3 + CloudFront
Static hosting and CDN delivery for the Clara chat widget bundle.
Docker Compose
Local stack with a ClickHouse cluster, ClickHouse Keeper nodes and Redis.
Cloudflare Pages
Hosting for the static adpal.com marketing site after its move off Webflow.
Amazon MSK
Managed Kafka for the event pipeline
ClickHouse cluster (CDK)
Its own infrastructure stack with autoscaling
Kubernetes
ClickHouse operator with Keeper, and Redpanda alongside it
LocalStack
The retrieval service runs end to end locally without a cloud account
SIP edge
Telephony infrastructure as its own CDK stack

Integrations & Payments

Stripe
Billing plans, subscriptions and payment webhooks for AdPal workspaces.
HubSpot
CRM sync of contacts, companies, deals, pipelines and associations from the platform.
Google Ads, Search Console, Drive
OAuth connections for campaign data, search queries, offline conversions and bot training files.
Telegram, WhatsApp, Instagram, Messenger
Messaging channels where the same trained assistant answers visitors and captures leads.
AWS SES + MJML
Transactional email built from MJML templates and sent through Amazon SES.
Facebook Business SDK
Campaign management on the ad side
Slack (Web and Events APIs)
Assistant and notifications inside Slack workspaces
Lighthouse, Wappalyzer, metascraper
Site audits: performance, detected technologies and page metadata

CMS & Commerce

Shopify app
Embedded Shopify app with a strict-sandbox web pixel extension that records order conversions.
WordPress plugins
Plugins that add AdPal traffic tracking and monitoring to WordPress sites.

Tooling & QA

Vitest
Unit tests for controllers and utilities in the Next.js portal.
Playwright
End-to-end browser tests for the AdPal Support assistant.

Engineering highlights

Storage was consolidated on ClickHouse: a short-lived Prisma and Postgres layer was replaced by a ClickHouse adapter in February 2024, so analytics, leads and chatbot embeddings share one columnar store queried with vector distance functions.

Campaign context flows into the conversation: UTM parameters reach the bot, so answers adapt to the campaign, keyword and page a visitor arrived from.

Attribution closes the loop with ad spend: contacts marked as quality leads keep their Google click ID and are exported as an offline-conversion feed for Google Ads.

The platform grew from a Laravel and Angular portal into one Next.js codebase, with the portal, the Shopify app and the chat widget each deployed as its own AWS CDK stack.

Retrieval earned its own runtime: the Python service with Qdrant, a PDF parser that keeps tables and images, and a queue-fed Lambda worker exists because document quality, not model choice, is what decides whether an assistant answers correctly.

The event pipeline is built like an analytics product rather than a feature: SDK, ingest API, Kafka, ClickHouse and GeoIP, which is why the portal can answer questions by campaign, keyword, device and hour instead of showing totals.