my-fullstack-ai-platform/.agents/PRD.md

483 lines
37 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 📄 Product Requirements Document (PRD)
# Teklifsat.com AI-Driven Lead Generation Platform
**Version:** 1.0
**Date:** 2026-04-06
**Author:** Product & Engineering
**Status:** Draft Awaiting Review
---
## 1. Executive Summary & Vision
### Vision Statement
> _"To become Turkey's #1 AI-driven lead generation platform by empowering 1,000 industrial companies within 6 months to scale globally via automated LinkedIn prospecting."_
### Product Overview
**Teklifsat.com** is a Multi-Tenant SaaS platform that automates the entire B2B ABM (Account-Based Marketing) pipeline from LinkedIn company prospecting to AI-powered qualification and personalized multi-contact outreach enabling Turkish industrial companies to scale into international markets (DE, EN, TR).
### Core Value Proposition
- **Fully automated pipeline:** Search → Enrich → Qualify → Outreach zero manual research
- **AI Qualification:** Intelligent lead scoring based on ICP, company profile, and enriched data
- **Built-in Translation Engine:** Real-time translation of chat messages and dynamic content across EN/DE/TR
- **Human-in-the-Loop (HIL):** Users retain full control over outreach before sending
### Differentiator
Unlike Apollo.io, Lemlist, or Instantly, Teklifsat focuses on **end-to-end AI pipeline automation** with native **multi-language support** purpose-built for companies operating across language barriers.
---
## 2. Target Audience & User Personas
### Primary Market
Turkish industrial companies (manufacturing, export, B2B services) expanding into German-speaking and English-speaking markets.
### User Roles
| Role | Description | Permissions |
| --------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Owner** | Company admin, creates the tenant | Full access. Manages Team Settings (ICP, Company Profile, Permissions). Can configure who may create campaigns (default: Owner only). |
| **SDR / Team Member** | Sales representative | Views all campaigns & leads within the tenant. Can create campaigns **if Owner grants permission**. Sends outreach (HIL). Manages own LinkedIn account connection. |
### User Stories
#### Owner
- _As an Owner, I want to define my company's ICP and profile once, so all campaigns use consistent qualification criteria._
- _As an Owner, I want to control which team members can create campaigns._
- _As an Owner, I want to see all campaign results across my organization._
- _As an Owner, I want to upload my company logo so the platform feels branded for my organization._
#### SDR / Team Member
- _As an SDR, I want to review AI-qualified companies (leads) and their associated contacts before sending outreach._
- _As an SDR, I want to chat with connected contacts in my preferred language (DE/TR/EN) with automatic translation._
- _As an SDR, I want to see the status of connection requests (pending, accepted, no contact) for each contact._
- _As a Team Member, I want to upload my profile picture so my colleagues can identify me easily in the dashboard._
---
## 3. Functional Requirements
### 3.1 Campaign Management
| ID | Requirement | Priority |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| F-CM-01 | Users can create campaigns with parameters: **Industry, City, Country, Company Size (Multiple Selection), Target Lead Count, Name** | P0 |
| F-CM-02 | Campaign parameters are **immutable** after creation (except Name and Target Lead Count) | P0 |
| F-CM-03 | Campaigns can be **paused** and **resumed** | P0 |
| F-CM-04 | Campaign has two phases: **Pipeline Phase** (automated) and **Engagement Phase** (manual/HIL) | P0 |
| F-CM-05 | Pipeline Phase auto-completes when target reached OR search results exhausted | P0 |
| F-CM-06 | Paused campaigns are excluded from job scheduling | P0 |
| F-CM-07 | **Strict Uniqueness:** Within a tenant, the combination of `industry + country` is the unique identifier for a campaign. Users cannot create a second campaign for the same combination; to target more leads, they must use the **Top-Up approach** (adjusting `target_leads` on the existing campaign). | P0 |
| F-CM-08 | **Lead Adjustment:** Users can adjust `target_leads` on an existing campaign (increase or decrease). - **Increase:** The pipeline resumes automatically if it was `pipeline_completed` and the new target exceeds `current_leads`. - **Decrease:** Permitted as long as `new_target_leads >= current_leads`. If `new_target_leads` is set equal to `current_leads`, the campaign transitions to `pipeline_completed` immediately. | P0 |
| F-CM-09 | **Quota Guard on Top-Up:** When increasing `target_leads`, the system must validate that `tenant.current_leads_this_month + (new_target - old_target)` does not exceed `tenant.monthly_quota`. If exceeded, the top-up is rejected with a clear error message. | P0 |
| F-CM-10 | **Quota Guard on Create:** When creating a campaign, the system must validate that `tenant.current_leads_this_month + target_leads` does not exceed `tenant.monthly_quota`. | P0 |
#### Campaign Status Model
```
Pipeline Phase: pending → running → paused → pipeline_completed
↑ (resumes on lead top-up if pipeline_completed and new target > current_leads)
Engagement Phase: ongoing (after pipeline completes, users engage with leads)
```
#### Quota Logic
```
tenant.monthly_quota = total credits available this month (e.g. 1,000)
tenant.current_leads_this_month = SUM of current_leads across all campaigns of this tenant this month
remaining_quota = monthly_quota - current_leads_this_month
```
- Quota is based on **delivered qualified companies** (`current_leads`), not ordered lead counts (`target_leads`)
- Monthly quota resets at the start of each calendar month
- The pipeline auto-stops when `tenant.current_leads_this_month >= monthly_quota`, even if individual campaign targets are not yet reached
- Affected campaigns are paused with status `quota_reached` until the next month or quota upgrade
### 3.2 Lead Pipeline (Background Jobs)
| ID | Requirement | Priority |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| F-PL-01 | **Company Search:** Suche nach Unternehmen über Unipile API (LinkedIn Premium) basierend auf Branche, Standort und Firmengröße. | P0 |
| F-PL-02 | **Company Enrichment (Global Cache):** Website-Inventur via Firecrawl `/map`. Ein AI-Agent führt das Scraping durch. Daten werden global in `leads` gespeichert. Liegt eine Anreicherung vor (< 30 Tage), wird dieser Schritt übersprungen. | P0 |
| F-PL-03 | **Company Qualification (Tenant-Specific):** KI bewertet das Unternehmen basierend auf (globalen) Enrichment-Daten, Tenant-ICP und Firmenprofil. Dieser Schritt ist IMMER tenantspezifisch. **Billable Event:** Sobald qualifiziert, wird 1 Credit abgezogen. | P0 |
| F-PL-04 | **People Search (Conditional):** Nur für **qualifizierte** Unternehmen wird eine Suche nach Personen (Contacts) gestartet (Senioritäts-Level: CXO, Director, etc.). Ein Lead (Company) kann mehrere Kontakte haben. | P0 |
| F-PL-05 | **Outreach Strategy Generation:** Automatische Erstellung der personalisierten Outreach-Strategie/Template für das Unternehmen (mit Platzhaltern für Kontaktnamen). | P0 |
| F-PL-06 | Pipeline runs in **batches** (510 companies per run), loops until target reached. | P0 |
| F-PL-07 | Companies (Leads) are **deduplicated globally** by their LinkedIn Company URL. General enrichment data is shared across all tenants to save costs. | P0 |
| F-PL-08 | **Qualification** and **Outreach Strategy** are strictly **campaign-specific** (stored on `campaign_leads`). Enrichment data is globally shared but can be refreshed if TTL expired. | P0 |
### 3.3 LinkedIn Engagement (V2)
| ID | Requirement | Priority |
| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| F-LE-01 | Send **connection request** with name-personalized outreach message via Unipile API (triggered by user click HIL). | P1 |
| F-LE-02 | Track connection status per **Contact** via Unipile webhooks: `pending → accepted → no_contact`. | P1 |
| F-LE-03 | If no response after **3 weeks**: retry connection request once. Still no response mark contact as `no_contact`. SDR can then try the **next contact** in the same company. | P1 |
| F-LE-04 | After connection accepted: enable **chat messaging** with the specific contact. | P1 |
| F-LE-05 | **Inbox View:** Display LinkedIn conversations per contact. | P1 |
| F-LE-06 | Track outreach performance (open rates, response rates) per company and per SDR. | P1 |
| F-LE-07 | **Claim Logic:** A contact is marked as `claimed_by_id` when an SDR starts the engagement. | P1 |
### 3.4 Translation Layer (V2)
| ID | Requirement | Priority |
| ------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| F-TL-01 | **i18n:** Static UI elements in EN, DE, TR | P1 |
| F-TL-02 | **Dynamic Content Translation:** Company and contact data, enrichment results, outreach messages translated to user's preferred language | P1 |
| F-TL-03 | **Outreach Language:** Outreach messages generated/translated into contact's language (detected from LinkedIn profile) | P1 |
| F-TL-04 | **Chat Translation:** Incoming messages translated to user's language. Outgoing messages translated to contact's language before sending. | P1 |
### 3.5 Team & Tenant Settings
| ID | Requirement | Priority |
| ------- | --------------------------------------------------------------------------------------------------------- | -------- |
| F-TS-01 | **ICP Definition:** Owner defines Ideal Customer Profile per tenant (used for AI qualification) | P0 |
| F-TS-02 | **Company Profile:** Owner defines own company's products/services (used for AI qualification + outreach) | P0 |
| F-TS-03 | **Permissions:** Owner configures who can create campaigns (default: Owner only) | P0 |
| F-TS-04 | **Team Management:** Invite/remove team members (existing feature) | P0 |
| F-TS-05 | **Organization Branding:** Ability to upload an organization image (Company Logo). | P1 |
### 3.6 User Settings
| ID | Requirement | Priority |
| ------- | ---------------------------------------------------------------------------------- | -------- |
| F-US-01 | User selects preferred language (EN/DE/TR) | P0 |
| F-US-02 | User connects LinkedIn account via Unipile (existing feature) | P0 |
| F-US-03 | **Profile Picture:** Ability for team members to upload their own profile picture. | P1 |
### 3.7 Onboarding Automation (V3)
| ID | Requirement | Priority |
| ------- | ---------------------------------------------------------------------------------------------------- | -------- |
| F-OB-01 | User provides own website URL system scrapes and auto-generates company profile via Firecrawl + AI | P2 |
| F-OB-02 | User provides 3 customer URLs system scrapes and auto-generates ICP via Firecrawl + AI | P2 |
| F-OB-03 | Show progress animation during scraping ("Your profile is being created…") | P2 |
---
## 4. Non-Functional Requirements
### 4.1 Rate Limiting & Resource Management
#### LinkedIn / Unipile (User-Owned Premium Business Accounts)
All search and engagement actions are performed using the **user's own LinkedIn Premium Business account**. Platform-owned Sales Navigator accounts are no longer used.
| Action | Premium Business Limit | Handling Strategy |
| ------------------------------------ | ---------------------- | ----------------------------------------------------------------------------- |
| **Search Profiles (Pipeline Phase)** | **1,000** total/query | Max 1,000 results per query. Use persistent `search_cursor` (base64). |
| **Daily Search Limit** | **1,000** profiles/day | Unipile recommendation. **Safe Mode**: Default to 200/day. |
| **Connection requests (with note)** | 80100/day, ~200/week | User clicks 'Send' (HIL). Max **300 characters**. |
| **Randomized Delays (Critical)** | Required | Spaced calls (30120s+ delay) rather than fixed intervals to avoid detection. |
| **Account Warmup** | Required | Start new/inactive accounts with 20% of limits, ramping up over 14 days. |
> [!IMPORTANT]
> **Account Safety FIRST**: Since users connect their primary business accounts, the platform enforces human-like behavior. Avoid fixed-interval polling. Use the `new_relation` webhook for status updates instead of polling the invitation list.
#### Implementation Requirements
| Requirement | Details |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Search Account** | Each campaign is assigned exactly **one** `search_account_id` (the SDR who initiated the search). This account provides the search quota. |
| **Engagement Phase (1:N)** | While the search is tied to one account, **any** SDR in the tenant can claim a lead and use their own `linkedin_account_id` for the outreach/engagement phase. |
| **429 / 500 / 422 Handling** | 429/500 (Rate limit) Pause account search for 24h. 422 (Cannot resend) Mark lead as `invitation_limit_reached`, retry next day. |
| **Cursor Workflow** | The Inngest search loop persists the `search_cursor` to DB after every batch, enabling reliable pause/resume across sessions. |
#### Firecrawl
| Resource | Constraint | Handling Strategy |
| --------------- | ------------------------- | --------------------------------------------------------------------------------------------------------- |
| **Concurrency** | Max 2 concurrent browsers | **Semaphore pattern**: acquire/release slot via atomic DB update. Jobs wait + retry if no slot available. |
---
### 4.2 Scalability
- **Linear Scaling**: Total platform capacity increases automatically as more users connect Premium accounts.
- **Distributed Load**: Background jobs are distributed across user accounts, reducing the risk of a single "platform-wide" ban.
---
### 4.3 Multi-Tenancy
- **Row Level Security (RLS)** on all tenant-scoped tables.
- **Lead (Company) Ownership**: Companies found within a tenant are shared.
- **Contact Ownership**: To prevent SDRs from messaging the same person, a contact is marked as `claimed_by_id` (SDR User ID) once an outreach or chat is initiated.
---
### 4.4 Reliability
- **Persistent Cursors**: Inngest resume logic uses `campaigns.search_cursor` to prevent duplicate profile fetches and credit waste.
- **Webhook-Driven**: Replaces polling for connection acceptance, reducing account activity.
---
## 5. Data Model
---
## 6. Architecture & Technology Stack
### 6.1 Unipile Implementation Constraints
- **User-Owned Only:** Platform Managed Service is removed. All actions are scoped to user-connected accounts.
- **Search Logic:** Uses the standard `linkedin/companies` or `linkedin/people` search endpoint with the `search_cursor` parameter.
- **Rate Limit Resilience:** Search accounts tracks daily fetches. If a 429/500 occurs, the account is temporarily blocked in our DB (`user.blocked_until`).
- **Distributed Outreach:** One campaign can have multiple SDRs engaging leads, each using their own `linkedin_account_id`.
- **Randomized Delays:** Minimum 30s-120s delay between profile fetches. Max 100 profiles total per search batch for safety.
- **Filter: Headcount (Company Size):** Passed as an array of objects `[{"min": x, "max": y}]`.
- **Valid Min:** 1, 11, 51, 201, 501, 1001, 5001, 10001
- **Valid Max:** 10, 50, 200, 500, 1000, 5000, 10000
- **Filter: Industries & Locations:** These parameters must be retrieved as IDs from the Unipile `GET /api/v1/linkedin/search/parameters` endpoint before campaign creation.
- **Search UI Constraints:**
- **Debounced Input:** Minimum 300ms delay/buffer on search inputs to minimize Unipile API calls.
- **Campaign Deduplication:**
- **Logic:** Server-side check before creation to prevent multiple campaigns with identical `industries` + `locations` + `headcount` within the same organization.
- **Action:** If a match is found, prompt the user to use the existing campaign or confirm if they truly want a duplicate.
### Stack
| Layer | Technology | Purpose |
| ------------------- | --------------------- | -------------------------------------------------------- |
| **Frontend** | Next.js (App Router) | UI, Server Actions, API Routes |
| **Database** | Supabase (PostgreSQL) | Data persistence, RLS, Realtime subscriptions |
| **Background Jobs** | Inngest | Step-based workflows, retries, scheduling, rate limiting |
| **LinkedIn API** | Unipile | Search, connection requests, messaging, webhooks |
| **Web Scraping** | Firecrawl | Company website enrichment |
| **AI** | LLM API (TBD) | Lead qualification, outreach generation, translation |
| **Auth** | Supabase Auth | User authentication (existing) |
### Architecture Diagram
````
┌─────────────────────────────────────────────────────┐
Frontend (Next.js)
Campaign UI Lead Table Inbox Settings
└───────┬────────────────┬─────────────────┬──────────┘
┌───────────────┐ ┌───────────┐ ┌──────────────────┐
API Routes Supabase Supabase
/api/\* Realtime Webhooks (Unipile
└───────┬───────┘ └───────────┘ connection
status updates)│
└──────────────────┘
┌───────────────────────────────────────┐
Inngest (Job Orchestration)
campaign/start campaign/run (loop)
Steps:
1. Search (Unipile)
2. Enrich (Firecrawl) ──► Semaphore
3. Qualify (AI)
4. Generate Outreach (AI)
5. Update DB + Counters
6. Sleep (rate limit)
7. Re-trigger if target not met
└───────────────┬───────────────────────┘
┌───────────────────────────┐
Supabase (PostgreSQL)
+ Row Level Security
+ Resource Limits
└───────────────────────────┘
```
### Key Patterns
- **Event Recursion:** `campaign/run` processes one batch, then re-triggers itself (instead of `while(true)`)
- **Semaphore:** Firecrawl slot acquisition via atomic DB update
- **Circuit Breaker:** LinkedIn 429 → set `blocked_until` → all jobs auto-pause
- **Scheduler:** Central job that checks resources before dispatching campaign runs
---
## 7. Frontend & UX Design System
### 7.1 Design Philosophy & Aesthetics
To wow the user, the platform follows a **Modern Premium Dark UI** with the following characteristics:
- **Visual Style:** Glassmorphism (semi-transparent cards), subtle gradients (HSL-based), and consistent border radii (standardized via Tailwind variables).
- **Typography:** Modern sans-serif (e.g., *Outfit* or *Inter*) with clear hierarchy.
- **Interactivity:** Micro-animations for feedback (e.g., pulsing states during AI qualification, smooth transitions between list and detail views).
- **Responsive:** Mobile-first layout, ensuring SDRs can monitor campaigns and respond to chats on the go.
### 7.2 Core Views & UX Patterns
#### A. Central Dashboard (Overview)
- **At-a-Glance Stats:** Active Campaigns, Total Companies Qualified, This Month's Quota Usage.
- **Activity Stream:** Recent activity (e.g., "SDR A connected with Director B at Company C").
#### B. Campaign Detail View (ABM Dashboard)
This is the core workspace where the 1:N relationship (Company:Contacts) is mastered:
- **Level 1: The Account (Company):**
- Represented as a Card or Table Row.
- Includes Company Name, Domain, Industry, and an **AI-Qualification Badge** (High/Med/Low Match).
- **AI Reasoning Card:** A dedicated UI section (side-drawer or tooltip) that answers "Why does this match?" based on the `qualification_data`.
- **Enrichment Quick-View:** A summary of the Firecrawl results (Company USP, recent news, etc.).
- **Level 2: The Contacts (People):**
- Expandable list inside the Company Card.
- Profile pictures, job titles, and **one-click outreach button**.
- **Status Indicators:** Color-coded connection states (e.g., Green for Accepted, Yellow for Pending, Red for No Contact).
#### C. Outreach & Engagement Cockpit
- **Strategy Preview:** View the company-specific outreach strategy generated by the AI.
- **Message Personalization Viewer:** Real-time preview of how placeholders ({{firstName}}) are filled before sending.
- **Human-in-the-Loop (HIL) Action Bar:** Prominent "Send Connection Request" button.
#### D. Real-Time Pipeline Tracker
- **Pulse:** Live progress bar during the Search-Batch (5-10 companies). Shows: "Searching companies..." → "Enriching via Firecrawl..." → "AI Qualifying..." → "Finding People...".
- **Visual Feedback:** No static lists during background jobs. Users see the pipeline "working" in real-time via Supabase Realtime subscriptions.
### 7.3 Design Tokens (Mapping to `globals.css`)
- **Primary:** Premium Teal/Blue (instead of default Indigo).
- **Accent:** Amber for "Qualified" but not yet contacted.
- **Neutral:** Slate/Gray for non-interactive backgrounds.
- **Success:** Emerald for accepted connections.
---
## 8. Monetization
| Model | Details |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **Pay-per-Lead** | Users are billed per **qualified company (lead)**. |
| **Billable Trigger** | Lead status = `qualified`. Triggered once per company per tenant across campaigns. |
| **Non-billable** | Disqualified companies, companies still in enrichment/qualification phase, search for contacts. |
| **Quota Model** | Each tenant has a `monthly_quota` (credits). Quota tracks `current_leads` (qualified companies), not `target_leads`. |
| **Quota Enforcement** | Pipeline auto-stops when `current_leads_this_month >= monthly_quota`. |
| **Quota Upgrade** | Owner can upgrade their plan to increase `monthly_quota`. |
### Tenant Quota DB Fields
```
tenants:
monthly_quota INT -- e.g. 1000 (set by plan)
current_leads_month INT -- delivered qualified leads this month (resets monthly)
quota_reset_date DATE -- date of last monthly reset
````
> [!NOTE]
> Free tier / trial details TBD. Consider offering first N leads free for onboarding conversion.
> [!IMPORTANT]
> The uniqueness constraint `(industry + country)` on active campaigns is enforced at the **application layer** (before DB insert) AND as a **partial unique index** in PostgreSQL:
>
> ```sql
> CREATE UNIQUE INDEX campaigns_industry_country_unique
> ON campaigns (tenant_id, industry, country);
> ```
---
## 9. Roadmap & Milestones
### Phase 1 MVP (Core Pipeline & ABM Dashboard)
> **Goal:** End-to-end pipeline working. Users can create campaigns and review/engage companies and their contacts in a high-end UI.
**Backend & Infrastructure:**
- [ ] Database schema (campaigns, leads, lead_contacts) + ABM RLS
- [ ] Inngest setup + campaign/run workflow (the Step-Loop)
- [ ] Search step (Unipile Company Search)
- [ ] Enrichment step (Firecrawl + AI-Agent + Semaphore)
- [ ] Qualification & Outreach Strategy generation (AI)
- [ ] Contact Search step (Unipile People Search for qualified companies)
**Frontend & UI (Next.js):**
- [ ] Layout System (Modern Dark Navigation, Premium Design Tokens)
- [ ] Campaign Overview UI (List/Add Campaigns)
- [ ] **The "ABM Board":** Company Card list with expandable Contacts
- [ ] **AI-Match Drawer:** Showing the "Why?" (AI Reasoning) and Enrichment details
- [ ] Real-time Progress Tracking (Supabase Realtime integration)
- [ ] SDR Claim & Connection Trigger (HIL Mock/V1 Trigger)
- [ ] Organization Logo upload & display
- [ ] User Profile Picture upload & display
---
### Phase 2 LinkedIn Engagement & Translation
> **Goal:** Users can send outreach, track connections, and chat with contacts across languages.
- [ ] Connection request sending via Unipile (HIL user clicks send)
- [ ] Webhook handler for connection status updates (per Contact)
- [ ] Connection retry logic (3-week timeout retry once)
- [ ] Inbox / Chat view (Contact-centric)
- [ ] Chat translation engine (incoming/outgoing)
- [ ] i18n (Static UI: EN, DE, TR)
- [ ] Dynamic content translation (company data, enrichment results)
---
### Phase 3 Scaling & Onboarding Automation
> **Goal:** Reduce onboarding friction, scale infrastructure.
- [ ] Auto-onboarding: Website Company Profile (Firecrawl + AI)
- [ ] Auto-onboarding: 3 Customer URLs ICP generation
- [ ] Multi-account LinkedIn pool (round-robin, per-account limits)
- [ ] Analytics dashboard (conversion rates, outreach performance)
- [ ] Per-tenant quotas and fair scheduling
- [ ] Billing integration (pay-per-lead tracking)
---
## 10. Risks & Mitigations
| Risk | Likelihood | Impact | Mitigation |
| ------------------------------------------------- | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| LinkedIn blocks / 429 per account | High | Critical | Circuit breaker per account, `blocked_until` in DB, random call spacing, multi-account pool (V3) |
| LinkedIn 422 `cannot_resend_yet` (invitation cap) | High | High | Dedicated error handler, mark lead as `invitation_limit_reached`, retry next day |
| Outreach message >300 chars truncated | Medium | Medium | Enforce 300-char limit in AI prompt and UI |
| LinkedIn fake-account detection | Low (if using real accounts) | Critical | Policy: only real accounts allowed, gradual ramp-up for new accounts |
| Firecrawl latency (up to 5 min/lead) | Medium | Medium | Parallelization within semaphore limit (max 2), cache already-scraped domains |
| AI qualification accuracy | Medium | High | Manual override in UI, iterative ICP refinement, log qualification reasoning |
| Multi-tenant data leakage | Low | Critical | RLS on all tables, automated tests for tenant isolation |
| Unipile webhook delivery failure | Medium | Medium | Idempotent webhook handler, periodic light polling as fallback (randomized intervals) |
---
## 11. Open Questions
| # | Question | Impact |
| --- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
| 1 | Which LLM provider for qualification + outreach? (OpenAI, Anthropic, local?) | Cost, latency, quality |
| 2 | Free tier / trial offering details? | Onboarding conversion |
| 3 | GDPR/DSGVO compliance scope for V1? (Lead opt-out, data deletion) | Legal, EU market |
| 4 | **Resolved:** All search and engagement actions use the user's connected LinkedIn Premium account (`search_account_id`). Platform accounts are not used. | — |
| 5 | Outreach message tone/style configurable per tenant? (See Section 12) | Feature scope |
---
## 12. Future Roadmap & Feature Requests (ICE Box)
The following items were identified during architecture review and are deferred for post-MVP implementation:
| Feature | Description | Reason for Delay |
| ------------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| **Phase 0: Pre-Qualification** | Lightweight AI check (LinkedIn-only data) before expensive Firecrawl/Deep-Scraping enrichment. | Optimization for unit economics. |
| **Tone of Voice (DACH)** | Configurable "Formal (Sie)" vs "Informal (Du)" setting for outreach generation. | Critical for conversion in German B2B context. |
| **Advanced Filtering** | Add `job_title` to campaign filters. (Note: Seniority levels implemented in V1). | Requires more complex search query mapping in Unipile. |
```
```