CRAIL Hackathon 2026 — A multi-agent pipeline that researches companies, scores engagement fit, identifies prospects, and generates hyper-personalised outreach — in under 30 seconds.
Business development is a research-heavy discipline that scales badly. A typical outbound BD rep spends 2–3 hours per prospect before writing a single word of outreach:
- Google the company, open 10 tabs
- Browse LinkedIn for the right decision-makers
- Read through blog posts, press releases, and job listings hunting for pain-point signals
- Manually piece together a narrative that connects their problems to your solution
- Write a cold email that still sounds generic — because there wasn't time to go deeper
The result is inconsistent output, missed signals, and response rates that make the whole effort feel futile. Scaling the team multiplies the cost without multiplying the quality.
KS Business collapses that 2–3 hour cycle into a 30-second multi-agent pipeline. You enter a company name, your offering, and an optional URL. The system:
- Scrapes the company website, LinkedIn org profile, recent posts, open job listings, and key personnel — concurrently
- Runs a People Swarm: one enrichment agent per discovered person fires in parallel to assess seniority, role category, and BD relevance
- Embeds every gathered fact into a per-pipeline Qdrant vector namespace for retrieval-augmented synthesis
- Pauses for your review — prune irrelevant people, exclude noisy posts, inject context the agents couldn't find
- Synthesises an evidence-first intelligence report: pain points with severity, ICP fit score (1–100 across six dimensions), prospects with contact angles, competitive landscape, and traceable sources
- Generates per-prospect POC engagement plans and 5-type personalised outreach on demand — cold email, follow-up, LinkedIn message, LinkedIn connection request, or call script; configurable tone and word limit, personalization hook anchored on a real signal
- Embeds completed pipelines into a shared Qdrant collection that powers a cumulative Market Intelligence view
- Tracks deal outcomes to feed ICP calibration data back into future scoring
flowchart LR
Browser(["🖥️ Browser\nNext.js App Router"])
subgraph Backend["FastAPI Backend · Python 3.12"]
Auth["JWT Auth\n/api/auth/*"]
API["REST API\n/api/v2/*"]
Orch["Pipeline Orchestrator\nasyncio background task"]
Agents["Agent Layer\n15 specialised agents"]
DB[("SQLite\nks_business.db")]
end
subgraph External["External Services"]
Groq["☁️ Groq\nllama-3.3-70b-versatile"]
Qdrant["🗄️ Qdrant Cloud\nvector store"]
DDG["🔍 DuckDuckGo\nweb search"]
Web["🌐 Public Web\nwebsites · LinkedIn · news"]
end
Browser -->|"HTTP · JSON\ncookie auth"| Auth
Browser -->|"HTTP · JSON"| API
API --> Orch
Orch --> Agents
Agents <-->|"persist state"| DB
Agents -->|"LLM calls"| Groq
Agents -->|"embed + search"| Qdrant
Agents -->|"search queries"| DDG
Agents -->|"scrape"| Web
DB -->|"read results"| API
Every pipeline run is split into two phases with a human-in-the-loop checkpoint between them. All gather agents run concurrently via asyncio.gather. Every stage writes its output to SQLite before proceeding — no work is lost if a stage fails or the user pauses.
flowchart TD
Input(["🔍 User Input\ncompany · URL · offering · post config"])
subgraph Gather["⚡ PHASE 1 — GATHER · agents run concurrently"]
direction TB
WS["🌐 Website Scraper\nhomepage + /about /products /services /team"]
LI["💼 LinkedIn Agent\nJSON-LD org schema → size, HQ, founded, overview"]
PO["📣 Posts Agent\nsite blog → LinkedIn JSON-LD → DDG strict name filter"]
JO["🏢 Jobs Agent\nopen roles → hiring signals + tech stack clues"]
PE["👤 People Agent\ndiscovers names, titles, LinkedIn snippets"]
SW["🐝 People Swarm\none enrichment agent per person · asyncio.gather\nrole category · seniority · BD relevance"]
KW["🔑 Keyword Extractor · LLM\nkeywords · product areas · personas · tech signals"]
RW["🔍 Web Research · DDG / Tavily\nnews · competitive · financial · market — 4 angles"]
RI["🗂️ RAG Indexer · fastembed\nchunks + embeds all gathered data → per-pipeline Qdrant namespace"]
PE --> SW
WS & LI & PO & JO & SW --> KW --> RW --> RI
end
HC{{"⏸ HUMAN CHECKPOINT · status = awaiting_input\nReview people · exclude noisy posts/jobs · inject context\nPOST /api/v2/pipeline/id/continue"}}
subgraph Synth["🧠 PHASE 2 — SYNTHESIZE"]
direction TB
RR["🔎 RAG Retriever\nLLM builds targeted queries → fetches top-k chunks"]
IA["📊 Insights Agent · LLM\nevidence-first: pain points · ICP score · prospects\ntech stack · competitive landscape · recommended approach"]
VS["📦 Vector Store\nembeds company into shared market-trends Qdrant collection"]
RR --> IA --> VS
end
subgraph OnDemand["✨ ON DEMAND — per prospect"]
direction LR
PP["📋 POC Plan Agent · LLM\nobjective · approach · timeline\ntalking points · success metrics · risks"]
EG["✉️ Outreach Generator · LLM\n5 message types: cold email · follow-up · LinkedIn message\nLinkedIn connection · call script\n15 banned phrases enforced · specificity rules"]
PA["📦 Pitch Asset Agent · LLM\nexec summary · short + detailed cold emails\nLinkedIn note · 5 discovery talking points"]
DO["📊 Deal Outcome\nwon · lost · no-response · meeting-booked\nfeeds ICP calibration loop"]
end
MI["📈 Market Intelligence\nQdrant clustering → trend themes + cross-portfolio BD opportunities"]
Input --> Gather
Gather --> HC
HC --> Synth
IA -->|"identified prospects"| OnDemand
VS --> MI
The orchestrator (pipeline.py) runs as a single asyncio.create_task spawned from the FastAPI endpoint — the HTTP response returns immediately with a pipeline_id while the pipeline runs in the background. The frontend polls every 2.5 seconds.
sequenceDiagram
autonumber
actor User
participant API as FastAPI
participant Orch as Orchestrator
participant DB as SQLite
participant Gather as Gather Agents<br/>(concurrent)
participant LLM as Groq LLM
participant QD as Qdrant
participant UI as Frontend
User->>API: POST /api/v2/analyze
API->>DB: create_pipeline(status=pending)
API-->>User: { pipeline_id }
API->>Orch: asyncio.create_task(run_pipeline)
Orch->>DB: status = gathering
Orch->>Gather: asyncio.gather(website, linkedin, posts, jobs, people_swarm)
Gather-->>Orch: all scraped data
Orch->>LLM: KeywordAgent — extract themes
LLM-->>Orch: keywords dict
Orch->>LLM: ResearchAgent — 4 DDG/Tavily searches
LLM-->>Orch: research results
Orch->>QD: RAGIndexer — chunk + embed → per-pipeline namespace
Orch->>DB: status = awaiting_input + save gathered data
loop Poll every 2.5 s
UI->>API: GET /api/v2/pipeline/{id}
API->>DB: get_pipeline
DB-->>API: status + gathered data
API-->>UI: pipeline state
end
User->>API: POST /api/v2/pipeline/{id}/continue
API->>DB: status = insights (sync, before task fires)
API->>Orch: asyncio.create_task(resume_pipeline)
Orch->>QD: RAGRetriever — LLM builds queries → top-k chunks
Orch->>LLM: InsightsAgent — evidence-first synthesis
LLM-->>Orch: intelligence + prospects
Orch->>QD: VectorStoreAgent — embed into shared market-trends collection
Orch->>DB: status = complete + save intelligence + prospects
User->>API: POST /api/v2/pipeline/{id}/email
API->>LLM: OutreachGeneratorAgent — 5 message types · banned phrases enforced
LLM-->>API: outreach JSON
API->>DB: save_email
API-->>User: outreach with personalization_hook
User->>API: POST /api/v2/pipeline/{id}/deal-outcome
API->>DB: save_deal_outcome + update ICP calibration stats
| Agent | Tools / Libraries | Output |
|---|---|---|
| WebsiteScraperAgent | requests · BeautifulSoup4 · DDG (URL discovery) |
Pages: url, title, cleaned text |
| LinkedInAgent | requests · BeautifulSoup4 (JSON-LD <code> tags) |
Org schema: size, HQ, founded, description, founders |
| PostsAgent | requests · BeautifulSoup4 · duckduckgo-search |
Posts: title, text, url, source, date |
| JobsAgent | duckduckgo-search · requests |
Jobs: title, location, url, snippet |
| PeopleAgent + Swarm | duckduckgo-search · requests · Groq LLM (one call per person) |
People: name, title, seniority, role category, BD relevance |
| EnrichmentAgent | Apollo API (if set) → Hunter.io (if set) → pattern inference | Contact email, phone, social URLs |
| KeywordAgent | Groq LLM | Keywords, product areas, target personas, tech signals |
| ResearchAgent | duckduckgo-search / tavily-python (4 angle searches) |
Research results: title, url, snippet, angle |
| CrawlerAgent | requests · BeautifulSoup4 · duckduckgo-search |
Crawl findings for thin public footprints |
| RAGIndexer | fastembed (ONNX) · qdrant-client |
Embedded chunks in per-pipeline Qdrant namespace |
| RAGRetriever | Groq LLM (query planning) · qdrant-client (ANN search) |
Top-k retrieved chunks |
| InsightsAgent | Groq LLM · RAG context · 17 banned generic phrases | Intelligence report: pain points, ICP score, prospects, competitive analysis |
| VectorStoreAgent | fastembed · qdrant-client |
Company embedding in shared ks_business_intelligence collection |
| POCPlanAgent | Groq LLM · 7 deal-type detection · specificity rules | POC: objective, approach, timeline, talking points, risks |
| OutreachGeneratorAgent | Groq LLM · EMAIL_PLAYBOOK · 15 banned phrases | 5 message types, personalization hook anchored on real signal |
| PitchAssetAgent | Groq LLM (JSON mode · 4096 tokens) | Exec summary, 2 cold emails, LinkedIn note, 5 talking points |
| MarketTrendsGenerator | qdrant-client · scipy (k-means) · Groq LLM |
Trend clusters with theme, insight, BD opportunity |
flowchart LR
subgraph Serial["Sequential (must complete in order)"]
KW["Keywords"] --> RW["Research"] --> RI["RAG Index"] --> HC["⏸ Human Review"] --> RR["RAG Retrieve"] --> IN["Insights"]
end
subgraph Parallel["Concurrent — asyncio.gather"]
WS["Website"]
LI["LinkedIn"]
PO["Posts"]
JO["Jobs"]
subgraph Swarm["People Swarm"]
P1["Person 1"]
P2["Person 2"]
Pn["Person N"]
end
end
Parallel -->|"all results merged"| KW
The outreach generator produces one of 5 message types — chosen by the user based on the current stage of the sales motion. All 15 banned phrases are enforced on every output.
flowchart LR
R["Research signals\npain points · recent developments\nkeywords · POC value prop\nurgency trigger"]
U["User inputs\nsender · offering · message type\ntrigger event · pain focus\ntone · word limit"]
subgraph Prompt["Prompt Assembly"]
direction TB
F["Framework\nsharp opener → value bridge → 1 CTA"]
P["Playbook\n15 banned phrases · no generic claims\npersona detection · specificity rules"]
end
LLM["☁️ Groq LLM\nJSON mode · temperature 0.72"]
Out["Output (one of 5 types)\ncold_email · follow_up_email\nlinkedin_message · linkedin_connection\ncall_script\n+ personalization hook"]
R & U --> Prompt --> LLM --> Out
The 15 banned phrases (never appear in generated outreach):
I hope this finds you well, I wanted to reach out, touching base, circle back, game-changer, revolutionary, synergies, cutting-edge, leverage, at the end of the day, move the needle, low-hanging fruit, reach out, pain points, value proposition
KS Business uses JWT cookie authentication — fully stateless, no external auth provider.
sequenceDiagram
actor User
participant UI as Next.js
participant MW as Middleware
participant API as FastAPI Auth
User->>UI: GET /dashboard (unauthenticated)
MW->>UI: redirect → /login
User->>UI: POST /login (email + password)
UI->>API: POST /api/auth/login
API-->>UI: Set-Cookie: ks_token=<JWT> (HttpOnly, 7d)
UI->>UI: redirect → /dashboard
User->>UI: GET /analyze
MW->>API: verify cookie token
API-->>MW: user payload
MW->>UI: allow
- Passwords hashed with bcrypt (cost factor 12)
- Tokens are HS256 JWTs signed with
JWT_SECRET_KEY, 7-day expiry - Cookie is
HttpOnly,SameSite=Lax— no XSS exposure - Next.js middleware (
src/middleware.ts) protects all routes except/loginand/register
The frontend is built on shadcn/ui patterns — Radix UI primitives composed with class-variance-authority and Tailwind CSS design tokens. All colours are CSS custom properties in globals.css.
| Token | Value | Usage |
|---|---|---|
--primary |
243 75% 59% (#5B50F7 indigo-violet) |
Buttons, active states, links |
--sidebar |
232 36% 7% (#0A0C14 near-black) |
Sidebar background |
--background |
220 20% 97% (#F4F5FA) |
Page background |
--card |
0 0% 100% |
Card surfaces |
--success |
152 61% 40% (#27A862) |
High confidence, won deals |
--warning |
38 92% 50% |
Medium confidence, active |
--danger |
0 84% 60% |
Low confidence, failed, lost |
--radius |
0.75rem |
Border radius for all cards |
Components: Button (6 variants + 7 sizes) · Card / CardHeader / CardContent · Badge (14 variants) · Input (with optional icon slot) · Textarea · Select · Tabs (Radix) · Progress (Radix) · Skeleton · Separator
| Decision | Rationale |
|---|---|
Groq llama-3.3-70b-versatile |
~400 tok/s on the free tier — fast enough for 5–15 LLM calls per pipeline with real-time UI feedback |
fastembed BAAI/bge-small-en-v1.5 |
384-dim ONNX model, runs on CPU, no API key, ships as a binary — embeddings are free and sub-second |
| Qdrant Cloud free tier | Persistent hosted ANN vector search; backend stays stateless. Gracefully skipped if QDRANT_URL is absent |
SQLite via stdlib sqlite3 |
Zero extra dependencies — no Docker Compose, no Postgres. Fly.io mounts a persistent volume at /data/ks_business.db |
| JWT cookie auth (no OAuth) | Self-contained, no external provider, no rate limits. HttpOnly cookie means no token storage in JS — XSS-safe |
| 5-type outreach generator | Different sales stages need different formats. A cold email, a LinkedIn connection request, and a call script follow completely different rules — one generator shouldn't try to do all three |
| 15 banned phrases enforced | Generic openers ("I hope this finds you well") are the single biggest predictor of low reply rates. Enforcing a banlist at prompt level costs nothing and directly improves output quality |
| Concurrent gather phase | asyncio.gather across website, LinkedIn, posts, jobs cuts ~25 s serial scraping to ~5 s wall-clock time |
| People Swarm | Enriching 8 people one-at-a-time is 8× slower. One asyncio.create_task per person (capped at 8) reduces it to the slowest single call |
| Human checkpoint | The gather phase surfaces raw, unfiltered data. Pruning irrelevant people and noisy posts before synthesis directly improves the evidence the LLM reasons over — this is a quality gate, not a UX feature |
| RAG-grounded synthesis | Stuffing 15,000 characters of raw scraped text into one prompt dilutes signal and risks context-length errors. Embedding everything and retrieving the top-k chunks per query keeps the synthesis context compact and high-signal |
| Deal outcome tracking | Closing the loop — tracking won/lost/no-response outcomes against the ICP score creates a calibration dataset that improves future scoring without any manual tuning |
| shadcn/ui design system | CSS custom properties + cva components make it trivial to swap colour themes, support dark mode, or white-label the app without touching component logic |
| Layer | Technology |
|---|---|
| Frontend | Next.js 15 (App Router) · TypeScript · Tailwind CSS 3 · React 19 |
| UI components | shadcn/ui pattern — Radix UI primitives + class-variance-authority + design tokens |
| Backend | Python 3.12 · FastAPI · SQLite (stdlib sqlite3) |
| Auth | JWT (python-jose) · bcrypt · HttpOnly cookies |
| LLM | Groq llama-3.3-70b-versatile (JSON mode, temperature-tuned per agent) |
| Scraping | requests · BeautifulSoup4 |
| Web search | DuckDuckGo (duckduckgo-search) — Tavily optional |
| Embeddings | fastembed — BAAI/bge-small-en-v1.5 (384-dim, ONNX, CPU, no API key) |
| Vector DB | Qdrant Cloud free tier |
| Deployment | Frontend → Vercel · Backend → Fly.io (persistent /data volume for SQLite) |
BDDev/
├── backend/
│ ├── main.py # FastAPI app — all v1 + v2 endpoints, auth, CORS, Groq init
│ ├── auth.py # JWT/bcrypt auth — register, login, logout, /me
│ ├── db.py # SQLite CRUD — pipelines, prospects, emails, users, deal outcomes
│ ├── pipeline.py # Async orchestrator + market trends generator
│ ├── utils.py # extract_json() — JSON parsing, fence stripping, fallback
│ ├── agents/
│ │ ├── scraper.py # Website scraper (homepage + priority sub-pages)
│ │ ├── linkedin.py # LinkedIn JSON-LD org schema extractor
│ │ ├── posts.py # Recent posts — site blog → LinkedIn → DDG strict filter
│ │ ├── jobs.py # Open roles scraper → hiring + tech signals
│ │ ├── people.py # People discovery + parallel swarm enrichment
│ │ ├── enrichment.py # Contact enrichment (Apollo → Hunter → pattern inference)
│ │ ├── keywords.py # LLM keyword extraction from gathered context
│ │ ├── researcher.py # Multi-angle web research (DDG/Tavily, dynamic year)
│ │ ├── crawler.py # Deep crawl fallback for thin public footprints
│ │ ├── rag.py # Chunk, embed, index + LLM-query retrieval
│ │ ├── embedder.py # Shared fastembed + Qdrant client singletons
│ │ ├── insights.py # RAG-grounded synthesis — pain points, ICP, prospects (17 banned phrases)
│ │ ├── poc_plan.py # POC engagement plan per prospect (7 deal types, specificity rules)
│ │ ├── email_gen.py # 5-type outreach generator (15 banned phrases, persona detection)
│ │ ├── pitch.py # Full pitch-asset bundle (JSON mode, 4096 tokens)
│ │ └── vector_store.py # Cumulative market-trends embedding store
│ ├── Dockerfile
│ ├── fly.toml
│ └── requirements.txt
├── frontend/
│ └── src/
│ ├── app/
│ │ ├── page.tsx # Dashboard — KPIs + pipeline history table
│ │ ├── login/page.tsx # Login — JWT cookie auth
│ │ ├── register/page.tsx # Register — bcrypt password hashing
│ │ ├── analyze/page.tsx # Analysis form — agent stage preview, advanced settings
│ │ ├── pipeline/[id]/page.tsx # Live progress tracker + human review + results
│ │ ├── pipeline/[id]/prospect/[pid]/ # Prospect detail — POC plan + 5-type outreach + deal outcome
│ │ ├── trends/page.tsx # Market Intelligence — clustered BD trends
│ │ ├── linkedin/page.tsx # LinkedIn Intelligence Hub
│ │ ├── market-map/page.tsx # Market Map — ICP scored company grid
│ │ ├── contacts/page.tsx # Contacts — people across all pipelines
│ │ ├── outreach/page.tsx # Outreach Tracker — sequence status
│ │ ├── playbook/page.tsx # BD Playbook — saved plays + templates
│ │ └── brief/page.tsx # Morning Brief — daily BD digest
│ ├── components/
│ │ ├── Sidebar.tsx # Dark sidebar — KS Business brand, all nav
│ │ ├── AuthShell.tsx # Conditionally renders Sidebar (suppressed on /login, /register)
│ │ └── Feedback.tsx # Thumbs up/down on generated outputs
│ ├── contexts/
│ │ └── AuthContext.tsx # Auth state — user, loading, refresh, logout
│ ├── middleware.ts # Next.js middleware — redirects unauthenticated to /login
│ └── lib/api.ts # Typed API client — all endpoints + interfaces
├── .github/workflows/
│ ├── ci.yml # TypeScript check + backend import smoke test
│ └── fly-deploy.yml # Auto-deploy backend to Fly.io on push to main
└── DEPLOYMENT.md # Full Fly.io + Vercel + GitHub Actions CI/CD guide
JWT cookie authentication. Register with email + password (bcrypt, cost 12). Login sets an HttpOnly 7-day cookie. Next.js middleware redirects unauthenticated users to /login before any route renders. Logout clears the cookie server-side.
Enter a company name, optional URL, and a description of your offering. Deal size and priority tag the pipeline for dashboard segmentation. Advanced settings expose post lookback months and max posts — useful for surfacing recency signals in fast-moving sectors. The right panel shows every agent stage with an estimated runtime, so the user understands they're watching a real pipeline, not a spinner.
Polls every 2.5 seconds. The stage tracker shows all 8 stages; the active one pulses amber. On awaiting_input, the human review panel appears — gathered content (people, posts, jobs, crawl findings, website pages) grouped into cluster cards. The reviewer can exclude items by index and inject free-text context before clicking Continue to synthesis.
Once complete: full intelligence report with company overview, 88 px SVG engagement score ring, 6-axis ICP score breakdown, pain points with severity + evidence chips, BD opportunities, prospects grid, competitive landscape, tech stack, and clickable sources.
Three-section layout. The POC plan auto-generates on first load (~3 s). The outreach generator lets you pick one of 5 message types: cold email, follow-up email, LinkedIn message, LinkedIn connection request, or call script. Each type has a purpose-built prompt with the right length, format, and CTA constraints. The pain focus field selects which of the identified pain points to anchor on. The "Anchored on" badge shows exactly which real signal made the outreach non-generic.
Log the result with the Deal Outcome modal — won, lost, no-response, or meeting booked — to feed the ICP calibration loop.
Four KPI cards with staggered entrance animations (total pipelines, active, prospects identified, avg engagement score). The pipeline table shows every company with a live-pulse dot for active runs, a mini engagement score ring, status pill, and a direct View link.
Powered by the shared Qdrant collection. Requires 3+ completed pipelines to unlock clustering. Each cluster card shows the theme, the companies grouped into it, a market signal paragraph, and an emerald "BD Opportunity" callout. A progress bar shows how close you are to the 3-company threshold. Each cluster links directly to /analyze — the market map becomes a prospecting tool.
- Python 3.11+
- Node.js 20+
- Groq API key — free tier is sufficient
- Qdrant Cloud cluster — free tier, optional but required for Market Intelligence
# 1. Clone
git clone https://github.com/manideepsp/BDDev.git
cd BDDev
# 2. Backend
cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Edit .env — set GROQ_API_KEY and JWT_SECRET_KEY at minimum
uvicorn main:app --reload --port 8000
# 3. Frontend (new terminal)
cd frontend
npm install
npm run devOpen http://localhost:3000. You'll be redirected to /register to create your first account.
| Variable | Required | Description |
|---|---|---|
GROQ_API_KEY |
Yes | console.groq.com — free tier works |
JWT_SECRET_KEY |
Yes | Any random 32+ char string for signing JWT tokens |
QDRANT_URL |
No | Qdrant Cloud cluster URL — Market Intelligence disabled without it |
QDRANT_API_KEY |
No | Qdrant Cloud API key |
TAVILY_API_KEY |
No | tavily.com — higher-quality search; DDG is the default |
ALLOWED_ORIGINS |
No | CORS origins, comma-separated (default: localhost:3000) |
DB_PATH |
No | SQLite file path (default: ks_business.db; Fly.io uses /data/ks_business.db) |
APOLLO_API_KEY |
No | Apollo.io — contact enrichment (email/phone lookup) |
HUNTER_API_KEY |
No | Hunter.io — email pattern inference fallback |
Generate a secure JWT_SECRET_KEY:
python -c "import secrets; print(secrets.token_hex(32))"See DEPLOYMENT.md for the full Fly.io + Vercel + GitHub Actions CI/CD setup.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/auth/register |
Create account {email, password, full_name} |
POST |
/api/auth/login |
Login {email, password} → sets HttpOnly cookie |
POST |
/api/auth/logout |
Clear cookie |
GET |
/api/auth/me |
Current user info |
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v2/analyze |
Start a pipeline (async — gather phase runs in background) |
GET |
/api/v2/pipeline/{id} |
Poll status + gathered data + intelligence |
POST |
/api/v2/pipeline/{id}/continue |
Resume after human checkpoint |
GET |
/api/v2/pipelines |
List all pipelines |
GET |
/api/v2/pipeline/{id}/prospects |
Get identified prospects |
POST |
/api/v2/pipeline/{id}/poc-plan |
Generate POC plan for a prospect |
POST |
/api/v2/pipeline/{id}/email |
Generate personalised outreach (5 types) |
GET |
/api/v2/pipeline/{id}/emails |
Retrieve generated outreach |
POST |
/api/v2/pipeline/{id}/pitch-assets |
Generate full pitch bundle |
POST |
/api/v2/pipeline/{id}/deal-outcome |
Log deal outcome for ICP calibration |
GET |
/api/v2/trends |
Market intelligence clusters from Qdrant |
GET |
/api/stats |
Dashboard KPIs |
GET/PUT |
/api/company-profile |
Sender profile for email sign-offs |
POST |
/api/feedback |
Thumbs up/down on generated outputs |
{
"prospect_id": "string",
"sender_name": "string",
"sender_company": "string",
"sender_offering": "string",
"message_type": "cold_email | follow_up_email | linkedin_message | linkedin_connection | call_script",
"tone": "professional | conversational | bold",
"pain_focus": "string (optional — which pain point to anchor on)",
"trigger_event": "string (optional — pre-fill from recent_developments)",
"linkedin_quote": "string (optional — takes priority as opener)",
"word_limit": 150
}{
"prospect_id": "string",
"outcome": "won | lost | no_response | meeting_booked",
"notes": "string (optional)"
}| Method | Endpoint | Description |
|---|---|---|
GET |
/api/prospects |
List v1 prospects |
GET |
/api/prospects/{id} |
Get a v1 prospect |
PATCH |
/api/prospects/{id}/status |
Update status |
DELETE |
/api/prospects/{id} |
Remove |
Every agent is wrapped in try/except. The pipeline never crashes because one source is unavailable:
| Failure | Behaviour |
|---|---|
| LinkedIn 429 / blocked | linkedin_data = {"people": [], "error": "..."} — pipeline continues |
| Website 403 / timeout | website_data = {"pages": [], "error": "..."} — pipeline continues |
| DuckDuckGo rate-limited | Research results are partial; insights synthesise from LinkedIn + website data |
| Qdrant unreachable | Embedding skipped; synthesis falls back to direct context; Market Intelligence shows "not enough data" state |
| Groq 429 / error | Pipeline status set to failed; error message stored in SQLite and surfaced in the UI with the real exception detail |
| Apollo / Hunter unreachable | EnrichmentAgent falls back to email pattern inference (first.last@domain.com heuristic) |
JWT_SECRET_KEY missing |
Backend logs a startup warning; auth endpoints return 500 until the key is set |
The frontend surfaces partial results at every stage — people and posts appear as soon as gathering completes, not after synthesis finishes.
- WebSocket push instead of polling — eliminate the 2.5 s latency on stage transitions
- CRM export (HubSpot, Salesforce) — one-click prospect push with all intelligence fields mapped
- Email send integration (Gmail, Outlook OAuth) — send directly from the outreach generator panel
- Scheduled re-research — weekly drift detection when a company's signal profile changes significantly
- Team workspace — shared pipeline history, prospect assignment, deal tracking across users
- ICP calibration dashboard — visualise how deal outcomes correlate with ICP sub-scores over time
- Voice briefing — TTS summary of the intelligence report for listening on the way to a call