# πŸ“„ 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** (5–10 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)** | 80–100/day, ~200/week | User clicks 'Send' (HIL). Max **300 characters**. | | **Randomized Delays (Critical)** | Required | Spaced calls (30–120s+ 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. | ``` ```