Synaplan
Open-source AI platform for companies: self-hosted AI agents, GDPR-compliant chatbot widget, RAG knowledge base, WhatsApp and Outlook AI, MCP, multi-model routing. Docker, Kubernetes or hosted in Germ
翻訳待ち
One shared inbox, intelligent AI replies, automated lead qualification, and a pipeline built around conversations. Built on the official Meta WhatsApp Business Cloud API.
翻訳待ち
ビジネスTypeScript · PLpgSQLGitHub @DevCorescentオンライン所有者未確認
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Loading the full README. The saved excerpt is shown below.
AI-Powered WhatsApp CRM, Automation & Lead Management platform — Next.js 16 full-stack, Neon PostgreSQL, Groq Llama AI.
Prerequisites
• Node.js 20+
• Get the .env file from team lead (never commit it)Read the full README ↗AI-Powered WhatsApp CRM, Automation & Lead Management platform — Next.js 16 full-stack, Neon PostgreSQL, Groq Llama AI.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) — frontend + backend in one |
| Database | PostgreSQL on Neon (serverless, no setup needed) |
| ORM | Prisma v7 |
| Auth | NextAuth v5 — credentials + JWT (DONE) |
| AI | Groq — Llama 3.3 70B |
| Real-time | Pusher (WebSocket for live inbox) |
| Validation | Zod |
| UI | Tailwind CSS v4 + Lucide Icons |
| State / Data | TanStack React Query v5 |
.env file from team lead (never commit it)git clone <repo-url>
cd whatsapp_lead
npm install
npm run db:generate
npm run dev
npm run db:seed
Demo login: [email protected] / Demo@1234
| Variable | Description |
|---|---|
DATABASE_URL | Neon PostgreSQL — already set, do not change |
AUTH_SECRET | NextAuth JWT secret |
GROQ_API_KEY | Groq AI key — already set |
GROQ_MODEL | llama-3.3-70b-versatile |
WHATSAPP_PHONE_NUMBER_ID | Meta WhatsApp number ID (per tenant in DB) |
WHATSAPP_VERIFY_TOKEN | Webhook verification token |
WHATSAPP_APP_ID | Meta app ID — required for Embedded Signup |
WHATSAPP_APP_SECRET | Meta app secret — webhook HMAC and Embedded Signup code exchange. Server only |
WHATSAPP_ES_CONFIG_ID | Facebook Login for Business configuration ID for Embedded Signup |
WHATSAPP_API_VERSION | Graph API version. Defaults to v19.0; Meta recommends v25.0 for Embedded Signup |
WHATSAPP_REGISTER_PIN | Optional 6-digit 2FA PIN used to register newly onboarded numbers. Registration is skipped when unset |
ENCRYPTION_KEY | AES-256-GCM key for secrets at rest (access tokens, app secrets) |
PUSHER_APP_ID / PUSHER_KEY / PUSHER_SECRET | Pusher real-time |
NEXT_PUBLIC_APP_URL | http://localhost:3000 |
WHATSAPP_APP_SECRETmust never be exposed to the browser. The app ID and the Embedded Signup config ID reach the client through/api/integrations/whatsapp/config, which is authenticated and returns no secrets — do not addNEXT_PUBLIC_*copies.
Customers connect WhatsApp through Meta's official
Embedded Signup flow
rather than by pasting credentials. Settings → WhatsApp (and each card on
/businesses) shows a Continue with Facebook button that runs Meta's dialog;
the result is verified server-side and saved as a WhatsAppIntegration of the
business — one row per connected number, so a business can connect several.
whatsapp_business_management and whatsapp_business_messaging.WHATSAPP_ES_CONFIG_ID.<NEXT_PUBLIC_APP_URL>/api/webhook/whatsapp
with WHATSAPP_VERIFY_TOKEN as the verify token. Embedded Signup subscribes each
customer's WABA to this app, so every customer's traffic arrives at that one URL,
signed with WHATSAPP_APP_SECRET. Customers never configure a webhook themselves.| Step | Where | Graph call |
|---|---|---|
| Customer authorises, picks WABA + number | Meta's dialog | Facebook Login for Business |
| Exchange the 30-second code for a business token | /api/integrations/whatsapp/connect | GET /oauth/access_token |
| Confirm which WABAs the token really covers | same | GET /debug_token → granular_scopes[].target_ids |
| Confirm the phone number is on that WABA | same | GET /<WABA_ID>/phone_numbers |
| Point the customer's WABA at our webhook | same | POST /<WABA_ID>/subscribed_apps |
| Register the number for Cloud API | same | POST /<PHONE_NUMBER_ID>/register |
The waba_id and phone_number_id the browser reports are treated as hints and
re-derived from Meta before anything is written — see selectWaba() /
selectPhoneNumber() in lib/whatsappEmbeddedSignup.ts and the tests in
tests/whatsapp-embedded-signup.test.ts.
Manual credential entry is still available under Settings → WhatsApp → Manual setup
for workspaces that predate this flow and for deployments whose Meta app is not yet
approved. Hand-entered credentials are mirrored into a WhatsAppIntegration row
whenever they are saved, so they behave like any other connected number.
WhatsAppIntegration is the runtime source of truth (lib/whatsappIntegrations.ts,
rules in lib/whatsappIntegrationRules.ts):
| Path | Which number |
|---|---|
| Inbound webhook | metadata.phone_number_id → its active WhatsAppIntegration → tenant + business. A disconnected number is ignored. Numbers with no row fall back to the legacy Business/TenantSettings lookup. |
| Conversations | One thread per contact per number; Conversation.whatsappIntegrationId records it. |
| Manual replies, AI, chatbot flows, FAQ, opt-out, read receipts | The conversation's own number (resolveConversationWhatsAppCreds). If that number is disconnected the send is refused — never re-sent from a different number. |
| Campaigns, templates | The business's default number (resolveWhatsAppCreds), then legacy credentials. |
| Test / Disconnect | Per number (integrationId). Disconnect keeps the row (inactive, token wiped) and promotes the next number to default. |
Guarantees: a phone_number_id is globally unique (unique index; concurrent
connects of the same number get a 409), and a business has at most one default
(row lock + partial unique index).
This is the beginning of the README. Read the rest at the original. Read the full README ↗