FlowPilot is an English-language portfolio project that demonstrates a focused customer-operations workflow. It captures a lead, validates and prioritizes it, routes it through replaceable email and CRM adapters, and can continue the conversation through WhatsApp.
This is not paid client work, a live business, or a fully production-ready product. It is intentionally small enough to explain and audit.
- Responsive customer-operations landing page and example dashboard
POST /api/leadswith bounded JSON input, strict field validation, a honeypot, priority detection, and rate limiting- Resend email and generic HTTPS CRM webhook adapters
- Optional
wa.melink generation - Twilio and Meta WhatsApp webhook handling
- Twilio request-signature, Meta verification-token, and Meta payload-signature checks
- Node test coverage for validation, priority, HTML escaping, rate limiting, and signatures
- ESLint, production build, and GitHub Actions CI
No original business photos, customer vehicles, personal testimonials, addresses, or phone numbers are published.
Browser form
│
▼
POST /api/leads ── validation ── priority ── rate limiter
│ (injectable port)
├── ResendEmailAdapter
├── WebhookCrmAdapter
└── WhatsApp deep link
Twilio / Meta
│ signed webhook
▼
/api/whatsapp-webhook ── signature check ── bounded parsing ── reply
Routes own HTTP concerns. lib/lead-utils.js contains deterministic validation and formatting. lib/integrations.js isolates external providers behind small adapters. lib/webhook-security.js performs signature verification against the raw request. lib/rate-limit.js exposes a consume(key) contract so the default implementation can be replaced without changing the route.
Requires Node.js 20.9 or newer.
npm install
cp .env.example .env.local
npm run devOpen http://localhost:3000.
Quality checks:
npm test
npm run lint
npm run buildSee .env.example. Keep real credentials in the deployment platform, never in Git.
- Configure the Resend variables to deliver email.
- Set
CRM_WEBHOOK_URLto a credential-free HTTPS URL. UseCRM_WEBHOOK_TOKENfor bearer authentication. - Set
FLOWPILOT_WHATSAPP_NUMBERto enable the post-submitwa.melink. - For Twilio, configure the public webhook URL and set the exact same URL in
WEBHOOK_PUBLIC_URL; Twilio includes the URL in its signature. - For Meta, configure both
META_VERIFY_TOKENandMETA_APP_SECRET.
When a signing secret is configured, an absent or invalid signature is rejected. In production, missing Twilio or Meta signing secrets fail closed with a service-unavailable response. Development permits unsigned webhook payloads only when the corresponding secret is absent, making local fixture testing possible.
- Request bodies and every accepted field are length-bounded.
- Inputs must be strings; phone and email fields receive format checks.
- User content is HTML-escaped before entering email markup.
- Integration responses expose only coarse delivery states, not provider bodies, credentials, or exception details.
- CRM URLs must use HTTPS and may not embed credentials.
- HMAC values and verification tokens use timing-safe comparison.
- Meta signatures are calculated over the unmodified raw body.
- Twilio signatures include the externally visible URL and sorted form fields.
- Security headers are set in
next.config.mjs.
- The default rate limiter is process-local. It is useful for local development and a single long-lived process, but not globally consistent on serverless or horizontally scaled deployments. Inject a Redis/Upstash-backed implementation of
consume(key)before treating rate limits as an abuse-control boundary. - Webhook events are not persisted or deduplicated. Providers can retry, so production handling needs an event store and idempotency keys.
- Lead delivery is best effort and synchronous. A production system should enqueue accepted leads, retry with backoff, add dead-letter handling, and expose internal delivery observability.
- There is no authentication, operator dashboard, consent ledger, retention policy, deletion workflow, or regional compliance configuration.
- Priority detection is deterministic keyword matching, not a calibrated classification model.
- Provider API versions, WhatsApp template rules, and webhook schemas must be monitored over time.
- The CRM allowlist is deployment-controlled, not hardcoded. High-assurance deployments should enforce an explicit destination allowlist and egress policy.
MIT. See LICENSE.