Open-Source Cold Email & Warmup Platform
Warmbly 是開源的冷郵件平台:溫暖您的收件箱、發送活動、並對每個回覆進行分流。可在我們的雲端運行或自行託管整個系統。
AI 翻譯
尚未收集儲存庫資訊。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Find US business leads and manage them in a lightweight CRM.
LeadScout is a hosted SaaS (Next.js) plus a CLI that collects US business leads, scrapes contact details from each business's website, and exports everything to CSV or Excel.
Each account has its own lead pipeline at /app:
lib/crm.ts).backfillLegacyLeads in lib/leads.ts).Not built yet; the CRM comes first. Notes for when this becomes a priority:
email_accounts, plus email_templates,
campaigns, campaign_recipients, email_messages, and suppression_list. Sends
and replies should write to lead_activities and move leads to Contacted/Replied.Not live yet. Premium is granted by an admin on /admin; nobody can pay in the app.
The code from the credit-pack era is still in place (app/api/billing/checkout,
app/api/webhooks/paystack, lib/paystack.ts, lib/payments.ts, the payments
table, and the packs in lib/plans.ts), but credits no longer gate searches.
To do when payments become a priority:
lib/plans.ts,
the checkout route, and the webhook to grant plan = "premium" instead of credits.PAYSTACK_SECRET_KEY / NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY
in Vercel (Production only), and set the Paystack webhook URL to
https://leadscout.nocodeziggy.com/api/webhooks/paystack. The checkout callback uses
NEXT_PUBLIC_APP_URL.| Column | Description |
|---|---|
| Business Name | Name of the business |
| Niche | Friendly niche label (e.g. "Dental Practice") |
| State / City | City and state for the business |
| Phone / Website / Email | Public contact info, plus emails scraped from the website |
| Address | Best-effort full street address |
| Decision Maker | Top LinkedIn match (when SerpApi is configured) |
CRM exports also include Status, Saved, Contact Name, and Follow Up.
/admin) pick niches, states, and a limit, with no automatic daily cap.POST /api/search validates the request, inserts a queued row in searches, and
sends an Inngest event. It returns at once.lib/inngest/searchJob.ts) splits the search into state/niche
steps and runs 3 at a time (SEARCH_PARALLEL_NICHES). Each step queries Overpass,
scrapes websites, and saves its leads under a row lock on the job
(saveStep in lib/searchJobs.ts), so the daily cap and dedupe stay exact.GET /api/searches/:id. Closing the panel or the tab changes nothing.ALLOWED_COUNTRIES=NG (needs a country header from your host/CDN, e.g. Cloudflare).npm install
copy .env.example .env # then fill in values (see below)
# Create the database tables (drizzle-kit reads DATABASE_URL from .env.local)
npm run db:migrate
# Run the app, plus the Inngest dev server in a second terminal (set INNGEST_DEV=1)
npm run dev # http://localhost:3000
npx inngest-cli@latest dev -u http://localhost:3000/api/inngest
Required env vars (see .env.example for the full list):
| Variable | Where to get it |
|---|---|
DATABASE_URL | Free Postgres from neon.tech or Supabase |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY / CLERK_SECRET_KEY | Free app at clerk.com (dev can run keyless) |
PAYSTACK_SECRET_KEY / NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY | dashboard.paystack.com — use test keys first |
NEXT_PUBLIC_APP_URL | Your deployed URL (used for the Paystack callback) |
For Paystack webhooks, set the webhook URL in the Paystack dashboard to
https://<your-domain>/api/webhooks/paystack.
INNGEST_EVENT_KEY and INNGEST_SIGNING_KEY.npm run db:migrate against the production database before deploying new
migrations.master. In the Inngest dashboard, check the app is synced at
https://<your-domain>/api/inngest.Health check: GET /api/health.
# Default: dentist/salon/plumber/restaurant across all US states -> leads.xlsx
npm run cli
# Specific niches + states, CSV output
node src/cli.js --niches hvac,plumber,electrician --states US-TX,US-FL --limit 500 --out trades.csv
# See every available niche
node src/cli.js --list-niches
| Flag | Default | Description |
|---|---|---|
-n, --niches <list> | dentist,salon,plumber,restaurant | Comma-separated niche keys (--list-niches) |
-s, --states <list> | US | US/ALL for every state, or US-CA, CA, or California |
-l, --limit <number> | 2000 | Max total leads to collect |
-o, --out <path> | leads.xlsx | Output file path |
-f, --format <fmt> | inferred | csv or xlsx |
--delay <ms> | 1200 | Delay between Overpass queries (be polite) |
--no-enrich | off | Skip auto ratings enrichment even if SERPAPI_KEY is set |
The legacy localhost dashboard is still available via npm run web:legacy.
With SERPAPI_KEY in .env, the CLI adds Google ratings and review counts. The web
app keeps ratings off (SerpApi bills in USD, which doesn't fit the NGN price points)
but uses the same key for decision-maker lookups.
app/ # Next.js App Router (landing, dashboard, API routes)
api/search/ # queue a background search (returns at once)
api/searches/ # search history, job status/cancel, unseen result popup
api/inngest/ # Inngest endpoint that runs search steps
api/leads/ # CRM: list, bulk update/delete, per-lead detail, notes, export
api/billing/ # Paystack checkout
api/webhooks/ # Paystack webhook (HMAC-verified)
components/ # React components (dashboard, marketing)
lib/ # db (Drizzle), CRM (crm.ts, leads.ts), quota, paystack, plans, overpass cache
drizzle/ # generated SQL migrations
proxy.ts # Clerk auth + optional country gate
src/ # core lead engine (shared by web + CLI, plain ESM JS)
cli.js # CLI entry
runSearch.js # shared search pipeline
overpass.js # Overpass QL builder + fetch (retry, mirrors, optional cache)
parse.js # raw result -> lead record, dedupe
output.js # CSV / XLSX writers
server.js # legacy localhost dashboard (npm run web:legacy)
public/ # legacy dashboard assets