{"id":50559221,"url":"https://github.com/chatman-media/lead-engine","last_synced_at":"2026-06-14T09:01:21.388Z","repository":{"id":359601578,"uuid":"1241870653","full_name":"chatman-media/lead-engine","owner":"chatman-media","description":"AI sales-funnel engine — Telegram, WhatsApp \u0026 web widget, RAG, visa pipeline, admin UI, Postgres (monorepo)","archived":false,"fork":false,"pushed_at":"2026-06-11T10:49:40.000Z","size":25468,"stargazers_count":4,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T11:04:40.916Z","etag":null,"topics":["ai","bun","chatbot","monorepo","multichannel","postgresql","rag","recruitment","sales-funnel","telegram","typescript","whatsapp"],"latest_commit_sha":null,"homepage":"https://exchanges.agency","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chatman-media.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-17T23:09:10.000Z","updated_at":"2026-06-11T10:49:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"8077059c-e8b0-46c5-b6a9-f005b42238f8","html_url":"https://github.com/chatman-media/lead-engine","commit_stats":null,"previous_names":["chatman-media/lead-engine"],"tags_count":127,"template":false,"template_full_name":null,"purl":"pkg:github/chatman-media/lead-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Flead-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Flead-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Flead-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Flead-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chatman-media","download_url":"https://codeload.github.com/chatman-media/lead-engine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Flead-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34315075,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","bun","chatbot","monorepo","multichannel","postgresql","rag","recruitment","sales-funnel","telegram","typescript","whatsapp"],"created_at":"2026-06-04T10:30:41.475Z","updated_at":"2026-06-14T09:01:21.376Z","avatar_url":"https://github.com/chatman-media.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003ca name=\"top\"\u003e\u003c/a\u003e\n\n# Lead Engine\n\n**AI front office for messenger-first businesses**\n\n[![CI](https://github.com/chatman-media/lead-engine/actions/workflows/ci.yml/badge.svg)](https://github.com/chatman-media/lead-engine/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/chatman-media/lead-engine/actions/workflows/codeql.yml/badge.svg)](https://github.com/chatman-media/lead-engine/actions/workflows/codeql.yml)\n[![codecov](https://codecov.io/gh/chatman-media/lead-engine/graph/badge.svg)](https://codecov.io/gh/chatman-media/lead-engine)\n[![Security](https://github.com/chatman-media/lead-engine/actions/workflows/security.yml/badge.svg)](https://github.com/chatman-media/lead-engine/actions/workflows/security.yml)\n[![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6?logo=typescript\u0026logoColor=white)](https://www.typescriptlang.org/)\n[![Bun](https://img.shields.io/badge/Bun-1.3-fbf0df?logo=bun\u0026logoColor=black)](https://bun.sh/)\n[![PostgreSQL + RLS](https://img.shields.io/badge/PostgreSQL-RLS%20%2B%20pgvector-336791?logo=postgresql\u0026logoColor=white)](https://github.com/pgvector/pgvector)\n[![License: PolyForm NC 1.0.0](https://img.shields.io/badge/License-PolyForm%20Noncommercial%201.0.0-orange.svg)](LICENSE)\n[![Telegram](https://img.shields.io/badge/Telegram-bot%20%2B%20userbot-26A5E4?logo=telegram\u0026logoColor=white)](https://core.telegram.org/)\n[![WhatsApp](https://img.shields.io/badge/WhatsApp-Cloud%20API-25D366?logo=whatsapp\u0026logoColor=white)](https://developers.facebook.com/docs/whatsapp)\n[![Messenger](https://img.shields.io/badge/Messenger-Send%20API-0084FF?logo=messenger\u0026logoColor=white)](https://developers.facebook.com/docs/messenger-platform)\n[![VK](https://img.shields.io/badge/VK-Callback%20API-0077FF?logo=vk\u0026logoColor=white)](https://dev.vk.com/api/callback/getting-started)\n[![MAX](https://img.shields.io/badge/MAX-Bot%20API-111827)](https://dev.max.ru/docs-api)\n[![Stripe](https://img.shields.io/badge/Stripe-billing-635BFF?logo=stripe\u0026logoColor=white)](https://stripe.com/)\n\nTelegram Bot + Userbot · WhatsApp · Facebook Messenger · VK · MAX · Web Widget · BYOK LLM · RAG · operator handoff · provider marketplace\n\n**🟢 Live: [exchanges.agency](https://exchanges.agency)** \u0026nbsp;·\u0026nbsp; [admin](https://client.exchanges.agency) \u0026nbsp;·\u0026nbsp; [dev](https://dev.exchanges.agency)\n\n🌐 🇬🇧 **English** \u0026nbsp;·\u0026nbsp; [🇷🇺 Русский](README.ru.md) \u0026nbsp;·\u0026nbsp; [🇨🇳 中文](README.zh.md)\n\n\u003c/div\u003e\n\n---\n\nLead Engine is a **multi-tenant AI operations platform** for businesses that\nclose and fulfill work in messengers: Telegram, WhatsApp, Messenger, VK, MAX,\nand a web widget. It is not a FAQ bot. It turns a messy inbound chat into\nstructured requests, lead stages, knowledge-grounded replies, operator\ndecisions, partner handoffs, and audit trails.\n\nThe current product has two marketplaces:\n\n- **AI provider routing** — per-tenant BYOK configs for `chat`, `embed`,\n  `vision`, `judge`, `reranker`, and `transcribe` purposes.\n- **Service provider marketplace** — curated and custom providers that can be\n  installed into the tenant's service catalog, then routed to funnels, partner\n  services, webhooks, or manual operators.\n\nEvery tenant has isolated channels, LLM configs, knowledge base, funnels,\nservice catalog, partner ledger, and encrypted secrets. Isolation is enforced\nwith **Postgres Row-Level Security**, not only application filters.\n\n📖 **Docs:** [index](docs/README.md) · [Architecture](docs/engineering/ARCHITECTURE.md) · [Onboarding](docs/engineering/ONBOARDING.md) · [Service catalog](docs/engineering/SERVICE_CATALOG.md) · [Exchange](docs/engineering/EXCHANGE.md) · [Configuration](docs/engineering/CONFIGURATION.md) · [Roadmap](docs/strategy/ROADMAP.md)\n\n---\n\n## What Ships\n\n| Layer | What is live |\n|---|---|\n| Messenger channels | Telegram bot, Telegram userbot (MTProto), WhatsApp Cloud API, Facebook Messenger, VK community messages, MAX Bot API, WebSocket web widget |\n| AI routing | Per-tenant provider configs, encrypted BYOK keys, hot reload in API, separate purposes for chat / embeddings / vision / judge / reranker / voice transcription |\n| Retrieval | Hybrid RAG with pgvector, BM25, RRF, multi-query expansion, dynamic trimming, MMR, Jina/Cohere reranker, hallucination guard |\n| Workflows | Universal funnel backbone, AI funnel builder, drag-drop stages/fields, multi-request concierge flows, exchange rates/orders, operator-awaiting stages |\n| Service marketplace | Curated Phuket providers, custom providers, service catalog routes, partner services, partner deals, commission tracking, handoff modes |\n| Operator UI | Inbox, AI/human takeover, leads board, service catalog, partners, notifications, outreach, templates, audit log, diagnostics, admin copilot |\n| Security | Tenant RLS, AES-256-GCM secrets, webhook signature checks, rate limiting, audit records without raw secrets |\n\n---\n\n## Product Shape\n\n### Messenger layer\n\n| Kind | Inbound | Outbound | Notes |\n|---|---|---|---|\n| `telegram_bot` | Bot API webhook + `X-Telegram-Bot-Api-Secret-Token` | worker -\u003e Bot API | Auto-`setWebhook` when `PLATFORM_PUBLIC_URL` is set |\n| `telegram_userbot` | MTProto receive loop in `apps/api` | in-process userbot dispatcher | Per-tenant `api_id` / `api_hash`, fallback env supported |\n| `whatsapp` | Meta webhook + `X-Hub-Signature-256` | worker -\u003e Meta Graph | Per-tenant access token, verify token, app secret |\n| `facebook` | Messenger webhook + `X-Hub-Signature-256` | worker -\u003e Messenger Send API | Page Access Token, 24h response window rules apply |\n| `vk` | VK Callback API | worker -\u003e VK `messages.send` | Community messages, text-first MVP |\n| `max` | MAX Bot API webhook + `X-Max-Bot-Api-Secret` | worker -\u003e MAX `POST /messages` | Per-channel bot token and webhook secret; text-first MVP |\n| `web` | WebSocket `/ws/:slug` | in-process web dispatcher | Embed script + standalone demo client |\n\nInbound messages are validated, rate-limited, persisted in `tx1`, answered by\nLLM/RAG outside any DB transaction, then enqueued in `tx2`. The worker drains\n`outbound_queue` with `FOR UPDATE SKIP LOCKED`; web and userbot dispatchers stay\nin-process where the live connection lives.\n\n### AI provider routing\n\nEach tenant can configure a different model stack:\n\n| Purpose | Typical providers | Used for |\n|---|---|---|\n| `chat` | OpenAI, OpenRouter, Ollama; DB/UI also carries Anthropic slots | Replies, extraction, sales reasoning, tools |\n| `embed` | OpenAI / OpenAI-compatible endpoints, Ollama | KB ingestion and retrieval vectors |\n| `vision` | OpenAI, OpenRouter-compatible vision models | Image/document analysis, KYC/photo flows |\n| `judge` | OpenAI, OpenRouter, Anthropic | Quality lab, self-play, evaluation |\n| `reranker` | Jina, Cohere | Cross-encoder reranking after hybrid retrieval |\n| `transcribe` | OpenRouter, OpenAI-compatible APIs | Voice note transcription, including Groq via custom base URL |\n\nKeys live in `tenant_secrets` under AES-256-GCM. A key can be reused across\npurposes for the same provider, and changes apply without restarting `apps/api`.\n\n### Service provider marketplace\n\nThe catalog is the product surface for businesses that sell many services:\ntransfer, cleaning, massage, beauty, housing, exchange, custom offers, and\nanything a tenant adds.\n\nService catalog items route to one of four executors:\n\n| Route type | Meaning |\n|---|---|\n| `funnel` | Lead Engine owns the process: stages, fields, AI behavior, operator steps |\n| `partner_service` | A partner/provider executes the service; the platform tracks handoff and commission |\n| `webhook` | A request is sent to an external system |\n| `manual` | Operator handles it without automation |\n\nCurated marketplace providers install into three tables at once:\n`partners`, `partner_services`, and `service_catalog_items`. Custom providers\ncan be created from the UI when the curated shelf does not cover the tenant's\nreal executor network. See [SERVICE_CATALOG.md](docs/engineering/SERVICE_CATALOG.md).\n\n---\n\n## Vertical Packs\n\nLead Engine is vertical-agnostic at runtime, but ships with production-ready\nstarting points. Each pack seeds funnels, fields, prompts, and stage behavior.\n\n| Template | Business | Status |\n|---|---|---|\n| `exchange_v1` | Crypto/RUB -\u003e THB exchange desk | live, most active |\n| `concierge_v1` | Multi-service desk for villas, expats, hospitality | multi-request, provider handoff |\n| `recruitment_v1` | Recruitment and relocation agencies | GTM ICP |\n| `modeling_v1` | Modeling agencies | implemented |\n| `real_estate_v1` | Real estate sales | implemented |\n| `saas_v1` | SaaS sales pipeline | implemented |\n| `video_v1` | Video production | implemented |\n| `visa_v1` | Visa and immigration services | implemented |\n| `scooter_v1` | Scooter and bike rental | implemented |\n\nThe universal funnel backbone is:\n\n```text\ncapture -\u003e qualify -\u003e offer -\u003e [clear] -\u003e [fulfill] -\u003e won / lost\n```\n\nActive stages store `phase`; intake and terminal stages are anchors. The AI\nbuilder and `/api/admin/workflows/apply` validate monotonicity and required\n`qualify` / `offer` phases before saving a funnel.\n\n---\n\n## Demos\n\n| Demo | What it shows |\n|---|---|\n| `apps/landing` | Public product demos: exchange, concierge/service desk, provider marketplace, visa, vertical library |\n| `apps/api/demo/web-chat.html` | Standalone web-channel client for `/ws/:slug` |\n| `apps/api/scripts/seed-modeling-demo.ts` | Modeling vertical seed/demo data |\n| `docs/gtm/sales-bot/SETUP.md` | Meta-demo: a bot that sells Lead Engine itself |\n| `packages/kb/examples/*` | RAG examples with OpenAI or local Ollama |\n\nRun the landing demo with:\n\n```bash\nbun run dev:landing\n```\n\nRun the API/admin stack locally with the quick start below, then open the admin\nUI and install verticals/providers from the cabinet.\n\n---\n\n## Architecture\n\n| App / package | Responsibility |\n|---|---|\n| `apps/api` | Hono HTTP server: auth, admin API, webhooks, web widget WS, hot reload, metrics |\n| `apps/worker` | Outbound queue dispatcher, channel reload polling, cron jobs |\n| `apps/admin-ui` | React 19 + Vite cabinet: onboarding, channels, settings, catalog, leads, conversations, quality lab |\n| `apps/landing` | Public demo/marketing site |\n| `apps/widget` | Embeddable web chat widget for customer sites: vanilla TS, builds to a single IIFE `/widget.js` |\n| `apps/vertical-*` | Vertical template packages loaded through `packages/verticals` |\n| `packages/storage` | Drizzle schema, migrations, RLS helpers |\n| `packages/channel-*` | Channel adapters behind `ChannelAdapter` |\n| `packages/llm-router` | Provider clients and per-tenant routing |\n| `packages/kb` | RAG, ingestion, reranking, tools, vision helpers |\n| `packages/sales` | Styles, skills, stage classifier, coach/evaluation logic |\n| `packages/conversation-engine` | Inbound pipeline, DAL, `withTenant`, reply dispatch |\n| `packages/observability` | JSON logger and Prometheus metrics |\n\nDependency direction is intentionally acyclic; `conversation-engine` depends on\ndomain packages and channel contracts, while concrete apps wire adapters and\nroutes. Details: [ARCHITECTURE.md](docs/engineering/ARCHITECTURE.md).\n\n---\n\n## Quick Start\n\nRequires [Bun](https://bun.sh) 1.3.14+ and Docker.\n\n```bash\ngit clone git@github.com:chatman-media/lead-engine.git\ncd lead-engine\nbun install\n\ncp .env.example .env\n# Minimum:\n#   DATABASE_URL=postgres://lead:lead@localhost:5434/lead_engine\n#   PLATFORM_MASTER_KEY=\u003copenssl rand -hex 32\u003e\n#   TELEGRAM_WEBHOOK_SECRET=dev-tg-secret\n#   ALLOW_PUBLIC_SIGNUP=1   # local dev only\n\nbun db:up\nbun run apps/api/scripts/reset-and-migrate.ts\n\nbun run dev          # apps/api -\u003e http://localhost:3000\nbun run dev:worker   # outbound worker\nbun run dev:ui       # admin UI -\u003e http://localhost:5173\n```\n\nDefault local login after signup/reset flow: `bob@demo.io` / `test1234`.\nPublic signup is closed unless `ALLOW_PUBLIC_SIGNUP=1` is set.\n\nUseful commands:\n\n```bash\nbun db:up\nbun db:down\nbun db:reset\nbun db:psql\n\nbun run typecheck\nbun run test\nbun run check\n```\n\n---\n\n## Invariants\n\n- **RLS is mandatory.** Tenant-scoped production reads/writes must go through\n  `withTenant(db, tenantId, fn)`. In production the app DB role must be\n  `NOSUPERUSER NOBYPASSRLS`.\n- **No LLM call inside the long transaction.** `processInbound` persists first,\n  releases the DB connection, calls the LLM/RAG layer, then opens a second\n  transaction to enqueue outbound work.\n- **Hot reload is part of the product.** LLM configs, channels, and tenant\n  status changes apply immediately in `apps/api`; `apps/worker` reloads\n  channels by polling.\n- **Secrets never enter audit logs.** LLM keys, channel tokens, userbot\n  sessions, exchange requisites, and provider credentials stay encrypted in\n  `tenant_secrets`.\n\n---\n\n## Admin API\n\nAuthenticated endpoints live under `/api/admin/*`:\n\n- auth, invites, password reset\n- onboarding status\n- channel CRUD\n- LLM provider configs\n- KB documents\n- conversations and operator takeover\n- funnels, AI workflow builder, leads\n- service catalog, provider marketplace, partners, partner deals\n- exchange rates, requisites, orders\n- notifications, outreach, templates\n- audit log list and CSV export\n- billing, audit, diagnostics, quality lab, superadmin\n\nBrowse [`apps/api/src/routes/`](apps/api/src/routes) for route factories and\nintegration tests.\n\n---\n\n## Testing\n\nTests require Postgres on port `5434`.\n\n```bash\nDATABASE_URL=postgres://lead:lead@localhost:5434/lead_engine bun test\n```\n\nThe suite covers RLS enforcement, multi-tenant route isolation, webhook flows,\nchannel adapters, RAG, exchange workflows, service catalog/provider marketplace,\nquality lab, and the split-transaction pipeline. More:\n[TESTING.md](docs/engineering/TESTING.md).\n\n---\n\n## Deployment\n\nCurrent hosted environments:\n\n| Environment | URL | Notes |\n|---|---|---|\n| Production | `https://exchanges.agency` | landing, API/webhooks, `/healthz`, `/widget.js` |\n| Production admin | `https://client.exchanges.agency` | admin-ui on the subdomain root; `https://exchanges.agency/admin` redirects here |\n| Development | `https://dev.exchanges.agency` | separate dev instance; admin-ui remains under `/admin/` |\n\nCore env vars:\n\n| Var | Description |\n|---|---|\n| `DATABASE_URL` | Postgres connection string; app role must be `NOSUPERUSER NOBYPASSRLS` in prod |\n| `PLATFORM_MASTER_KEY` | 32-byte hex key for AES-256-GCM secrets |\n| `PLATFORM_PUBLIC_URL` | Public API URL for webhooks and snippets |\n| `TELEGRAM_WEBHOOK_SECRET` | Telegram webhook secret-token header |\n| `WHATSAPP_VERIFY_TOKEN` / `WHATSAPP_APP_SECRET` | Meta WhatsApp webhook fallback credentials |\n| `FACEBOOK_VERIFY_TOKEN` / `FACEBOOK_APP_SECRET` | Meta Messenger webhook fallback credentials |\n| `MAX_WEBHOOK_SECRET` | Optional MAX webhook secret fallback; per-channel secret is preferred |\n| `WEB_WS_AUTH_SECRET` | Optional web widget shared secret |\n| `KB_UPLOAD_DIR` / `KB_MAX_UPLOAD_BYTES` | Original KB upload storage path and per-file limit; use persistent storage in prod |\n| `STRIPE_*` | Optional billing |\n| `RATE_LIMIT_PER_MIN` / `RATE_LIMIT_PER_HOUR` | Inbound tenant rate limits |\n\nRun migrations under an owner/BYPASSRLS role; run apps under the restricted app\nrole. Full references: [CONFIGURATION.md](docs/engineering/CONFIGURATION.md)\nand [SERVER_RUNBOOK.md](docs/operations/SERVER_RUNBOOK.md).\n\n---\n\n## Positioning\n\n| Capability | Lead Engine | Intercom Fin | Chatbase | ManyChat |\n|---|:---:|:---:|:---:|:---:|\n| Telegram bot + personal account | ✅ | ❌ | ❌ | 🟡 bot only |\n| WhatsApp / Messenger / VK / MAX / Web | ✅ | 🟡 no VK/MAX/Telegram | 🟡 no VK/MAX/Telegram | 🟡 no VK/MAX/web chat |\n| BYOK LLM per tenant | ✅ | ❌ | ❌ | ❌ |\n| RAG + workflow stages | ✅ | 🟡 playbooks/procedures | 🟡 RAG + actions | 🟡 flow-builder + AI add-on |\n| Operator takeover | ✅ | ✅ | ✅ | ✅ |\n| Service provider marketplace | ✅ | ❌ | ❌ | ❌ |\n| Self-host / source-available | ✅ | ❌ | ❌ | ❌ |\n\nLegend: ✅ native fit · 🟡 partial or adjacent category · ❌ not native. Checked\nagainst public product docs in June 2026: [Intercom Fin channels](https://www.intercom.com/help/en/articles/13377077-choose-channels-to-deploy-fin-ai-agent),\n[Chatbase deploy/takeover](https://www.chatbase.co/docs/user-guides/chatbot/deploy),\nand [ManyChat channels](https://help.manychat.com/hc/en-us/categories/13556929063068-Channels).\n\nThe niche is messenger-native AI operations for RU/CIS/MENA and service-heavy\nbusinesses: not \"chatbot answers FAQ\", but \"messenger conversation becomes\nrevenue workflow\".\n\n---\n\n## Contributing \u0026 License\n\nUse [Conventional Commits](https://www.conventionalcommits.org/). Before\nshipping code, run:\n\n```bash\nbun run typecheck\nDATABASE_URL=postgres://lead:lead@localhost:5434/lead_engine bun test\n```\n\n**License:** the product is [PolyForm Noncommercial 1.0.0](LICENSE). Commercial\nuse requires a paid license from [chatman-media](https://github.com/chatman-media).\nReusable libraries in `packages/*` remain MIT. © Alexander Kireev / chatman-media.\n\n\u003cdiv align=\"center\"\u003e\n\n[🇷🇺 Русский](README.ru.md) \u0026nbsp;·\u0026nbsp; [🇨🇳 中文](README.zh.md) \u0026nbsp;·\u0026nbsp; [⬆ top](#top)\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchatman-media%2Flead-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchatman-media%2Flead-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchatman-media%2Flead-engine/lists"}