A verticalized WhatsApp agent for tradespeople. Voice or text in → signed PDF quotes and invoices out.
Tradespeople (plumbers, electricians, heating engineers) spend hours a week doing admin: quotes, invoices, chasing confirmations, hunting down past jobs. They live on WhatsApp. Typing into accounting software in the van, between clients, does not happen.
Manifold is an agent that lives inside WhatsApp. The tradesperson sends a voice note: "just finished at Mrs. Martin, 340 euros, replaced the kitchen faucet". Manifold extracts the client, the service, the amount, applies the right VAT rate, generates a compliant PDF invoice, and asks for confirmation — all in 10 seconds.
It's a narrow, opinionated, vertical agent. Not a chatbot. Not a general assistant. One job, done well.
- 🎙️ Voice-first — Whisper transcribes the voice note; written confirmation is requested for client names to avoid transcription errors on legal documents.
- 📝 Quote / invoice generation — Produces compliant French PDF (SIRET, VAT, payment terms, validity duration, legal penalties).
- 🔁 Status tracking — "Martin accepted", "Lefebvre paid", "cancel the last quote" — all handled conversationally.
- 🔍 Search old documents — "find the Dupont quote from January" — queries the DB, returns the match.
- 📧 Email delivery — Resend integration; sends the PDF directly to the client.
- 🏢 Multi-tenant — each tradesperson is identified by their phone number; full data isolation between businesses.
- 🚪 Self-serve onboarding — a new tradesperson is whitelisted by phone number, then the agent walks them through their company info on WhatsApp.
- ⚡ Rate-limited — 20 req/min, 100/hour per phone number.
| Layer | Choice | Why |
|---|---|---|
| Runtime | Node.js 20 · Express 5 · TypeScript | Boring, fast, typed |
| DB | PostgreSQL · Drizzle ORM | Typed queries, small surface |
| LLM | OpenAI GPT (agent loop) + Whisper (voice) | Best tool-calling reliability for a prod agent |
pdf-lib + @pdf-lib/fontkit |
Full control, no headless browser | |
| Resend | Modern deliverability | |
| Messaging | WhatsApp Cloud API (Meta) | Direct, webhook-based |
| Deploy | Railway | git push to prod |
┌──────────────────────┐ webhook ┌──────────────────────────┐
│ WhatsApp Cloud API │ ──────────▶ │ Express webhook handler │
│ (Meta) │ │ • dedup + rate-limit │
└──────────────────────┘ │ • voice → Whisper │
▲ │ • load business context │
│ signed PDF └────────────┬─────────────┘
│ │
│ ▼
│ ┌──────────────────────────┐
│ │ Agent loop │
│ │ system_prompt(business) │
│ │ ↓ │
│ │ GPT tool calls: │
│ │ • create_devis │
│ │ • create_facture │
│ │ • modify_document │
│ │ • send_document_email │
│ │ • update_status │
│ │ • list_documents │
│ │ • (+ onboarding tools) │
│ └────────────┬─────────────┘
│ │
│ ▼
│ ┌──────────────────────┐
└────── PDF + preview ◀──────│ pdf-lib renderer │
│ + Postgres storage │
└──────────────────────┘
git clone https://github.com/thomasetienne/manifold.git
cd manifold
cp .env.example .env # fill in the required keys
npm install
npm run db:push # applies the schema to your database
npm run dev # http://localhost:3000| Variable | Purpose |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
WA_ACCESS_TOKEN |
WhatsApp Cloud API token (Meta) |
WA_VERIFY_TOKEN |
Custom string echoed back during webhook verification |
WA_APP_SECRET |
Meta app secret (validates webhook signatures) |
OPENAI_API_KEY |
GPT + Whisper |
RESEND_API_KEY |
Email delivery |
npx tsx src/scripts/whitelist-plumber.ts 33612345678The tradesperson can then message the bot to start their onboarding conversation.
src/
├── agent/
│ ├── system-prompt.ts # Templated; reads business data from DB
│ ├── tools.ts # Tool schemas (OpenAI function calling)
│ ├── tool-executor.ts # Tool dispatch
│ ├── context.ts # Conversation / document context builders
│ ├── onboarding.ts # Step-by-step business setup flow
│ └── index.ts # Agent loop
├── db/ # Drizzle schema + repositories (businesses, clients, devis, factures, conversations)
├── middleware/ # Rate limiting, processing lock (dedup)
├── routes/ # webhook + health
├── scripts/ # seeds, migrations, whitelist, tests
├── utils/ # logger, numeric helpers
├── config.ts # env validation with Zod
└── index.ts # entry point
Why vertical, not general? General-purpose agents fail at boring, high-stakes document generation because they hedge. Tradespeople need a tool that commits: "this is the VAT rate, this is the legal mention, this is the PDF." Narrowing the domain lets the system prompt be precise, the tool surface be minimal, and the prompt engineering be actually maintainable.
Why voice-confirmed names? Whisper is excellent, but not for "Madame Lefèbvre-Grospiron". A single misspelled name on a legal invoice is painful. Manifold detects voice inputs, generates the doc from the transcription, then explicitly asks the tradesperson to confirm proper nouns in writing before finalizing.
Multi-tenancy via phone number. The tradesperson's personal WhatsApp number is the tenant key. No login, no password, no app to install. That's the point.
Reference implementation extracted from a production codebase. The domain model (French quotes/invoices, SIRET, VAT rates) is France-specific; extending to other jurisdictions is straightforward — replace system-prompt.ts and the PDF templates.
Built by @thomasetienne · part of the Momentum operating system for AI-native businesses.